The peers protocol is the internal P2P transport that lets every component of the Weble IoT Universal Gateway — drivers, logics, the web server, the UI, and remote integrations — share modules and exchange events as if they all lived inside the same Node.js process.
This page documents the client-side primitives you will use when writing code against the API. The wire format itself is an implementation detail and is not covered here.
var peers = require('peers')('myClientName')
The argument is the peer name you advertise on the mesh. It must be unique among the currently-connected peers. By convention:
'supervision' — it owns all the core modules.'driver_<type>_<id>'.'metricsExporter', 'mobileBackend', …).The function returned is both an object and a function. As an object it has methods (share, connect, listen, …). As a function it takes a peer name and returns a remote-peer handle:
peers.share('myModule', myObject) // expose myModule to other peers
peers('supervision').require('gateways', cb) // load the 'gateways' module from the 'supervision' peer
peers('supervision').on('newValue 4/7/25', cb) // listen to events from the 'supervision' peer
External clients never call peers.connect() directly — the connection is set up for you by the bootstrap returned from POST /auth. See Getting started for the full flow. After bootstrap, the peer object you receive is already connected and reconnects automatically if the link drops.
requirepeer('supervision').require('gateways', function(err, gateways){
if(err) return console.error(err)
gateways.writeValue('4/7/25', 1, function(err){
if(err) console.error('write failed', err)
})
})
require does a round-trip to the remote peer to ask for the list of methods of the named module. It returns a local proxy object with the same method names — calling any of them serializes the arguments, sends them over the socket, awaits the remote callback, and invokes your local callback with the result.
A few important things to know:
function(err, ...results){}.require the same module multiple times — each call returns the same proxy object after the first round trip.require and the new method appears.
shareIf your peer wants to expose its own functions to others on the mesh:
peers.share('temperatureSensor', {
read : function(callback){
callback(null, 21.7)
},
setUnit : function(unit, callback){
// ...
callback(null)
}
})
Any other peer can now require('temperatureSensor', cb) and get a proxy with read and setUnit.
You can pass an optional filter as the third argument to share — a function that returns true/false based on which peer is asking. Use it to expose a module only to specific clients.
peers.share('adminTools', adminToolsObject, function(callerPeerName){
return callerPeerName.startsWith('admin_')
})
on, once, emit, removeListenerEvery peer carries a shared EventEmitter that other peers can listen to. This is how the gateway pushes live updates (new values, gateway state changes, route errors, …) to clients.
// Listen to a specific event from a remote peer
peer('supervision').on('newValue 4/7/25', function(address, value, value_string, value_date){
console.log('value changed', value)
})
// Listen once
peer('supervision').once('gatewayState 12', function(state){ ... })
// Stop listening (always do this when the listener is no longer needed!)
peer('supervision').removeListener('newValue 4/7/25', myHandler)
// Emit on your own emitter — other peers can listen for this
peers.peerEvents().emit('myCustomEvent', 'arg1', 'arg2')
// emit() returns true if the call was actually delivered
var ok = peer('supervision').emit('hello', 'world')
Common event patterns used by the core modules:
| Event | Emitted by | Description |
|---|---|---|
newValue <addressKey> |
gateways |
Address value changed |
gatewayState <gatewayId> |
gateways |
Gateway connection state changed |
addressInserted / addressUpdated / addressDeleted |
gateways |
Address lifecycle |
gatewayInserted / gatewayUpdated / gatewayDeleted |
gateways |
Gateway lifecycle |
routeError <routeId> |
gateways |
Route execution failed |
loggerInserted / loggerUpdated / loggerDeleted |
loggers |
Logger lifecycle |
Every event emitted on the main mesh is automatically forwarded to every peer that has called .on(eventName) on it — there is no explicit subscription step.
The peers client itself emits events about its own connections:
peers.on('connect', function(peerName){ ... })
peers.on('connect supervision', function(){ ... }) // shortcut for a specific peer
peers.on('disconnect', function(peerName){ ... })
peers.on('disconnect supervision', function(){ ... })
peers.on('error', function(peerName, err){ ... })
Use the 'connect supervision' shortcut to know when the gateway is reachable for the first time — it is the safest place to start require'ing modules:
peers.connect('/tmp/supervision.sock')
peers.once('connect supervision', function(){
peers('supervision').require('gateways', function(err, gateways){
// ...
})
})
peers.connectedPeers() // → ['supervision', 'driver_modbus_1', 'webserver', ...]
peer('supervision').isConnected() // → true / false
peers.listenerCount() // total active listeners — useful for leak detection
Arguments and return values are serialized automatically. In addition to plain JSON values, you can pass:
Buffer instances — round-trip as binary on both sides.Error instances — reconstructed with message and stack preserved.Date objects.
Long-running peers clients almost always leak through forgotten event listeners. The best practice is:
on() with removeListener(). If your code path adds a listener, the matching cleanup path must remove it.once() when you only need a single notification.peers.listenerCount() in long-running services. A growing number is a leak.'connect supervision' — otherwise reconnects multiply your handlers.function attach(){
peer('supervision').require('gateways', function(err, gateways){
gateways.on('newValue 4/7/25', onValue)
})
}
function detach(){
peer('supervision').require('gateways', function(err, gateways){
gateways.removeListener('newValue 4/7/25', onValue)
})
}
peers.on('connect supervision', attach)
peers.on('disconnect supervision', detach)