
Research
Namastex.ai npm Packages Hit with TeamPCP-Style CanisterWorm Malware
Malicious Namastex.ai npm packages appear to replicate TeamPCP-style Canister Worm tradecraft, including exfiltration and self-propagation.
@pubkeeper/client
Advanced tools
@pubkeeper/client enables web browsers to join, consume, and produce content on a Pubkeeper network.
Brew : A protocol/method of delivery, e.g. WebSocket, SocketIO, WebRTC, HTTPBrewer : An object bound to a specific topic that accepts a data transports it to matched patrons.Patron : A consumer bound to one or more topics, it receives data from matched brewers.In a browser:
<script src="pubkeeper-client.js"></script>
Using npm:
$ npm install --save @pubkeeper/client
Then, load using ES5 require() syntax…
var PubkeeperClient = require('@pubkeeper/client').PubkeeperClient;
…or with ES2015+ import syntax:
import { PubkeeperClient } from '@pubkeeper/client';
import { PubkeeperClient } from '@pubkeeper/client';
import { WebSocketBrew } from '@pubkeeper/brew-websocket';
import { PubkeeperAesCbcCrypto } from '@pubkeeper/crypto-cjs-aes-cbc';
// Create a client
const client = new PubkeeperClient({
server: 'wss://192.168.100.191:9898/ws',
jwt: "[your JWT]",
crypto: new Map([
PubkeeperAesCbcCrypto,
]),
brews: [
new WebSocketBrew({
brewerConfig: {
hostname: '192.168.100.191',
port: 8000,
secure: true,
},
}),
],
});
client.connect();
Note: This client is the core pubkeeper client, as such it does not include any crypto or brews. If you want a pre-packaged version suitable for general usage see @pubkeeper/browser-client
To produce data into the Pubkeeper network you will need to create a brewer. The example below will setup a Brewer with the topic of topic.information
client.addBrewer('topic.information', (brewer) => {
// brewer has been *matched* by pubkeeper, i.e. there is
// at least one patron of this topic.
let counter = 0;
const id = setInterval(() => {
brewer.brewJSON({ counter });
counter += 1;
}, 1000);
return function teardown() {
// teardown function will be called when there are
// _no_ more patrons or this brewer has been removed.
clearInterval(id);
};
});
To listen to someone who is brewing information, you need to subscribe to the topic, and configure a callback to occur when there is data available for processing.
client.addPatron('weather.*', (patron) => {
// patron has been *matched* by pubkeeper, i.e. there is
// at least one brewer of this topic.
function handler(bytes, meta) {
console.log(meta.from, meta.topic, patron.id, bytes);
};
patron.on('message', handler);
return function teardown() {
// teardown function will be called when there are
// _no_ more brewers or this patron has been removed.
patron.off('message', handler);
};
});
The Patron message event is always fired with a Uint8Array as data, no matter which brew*() method was called on the brewing side. You need to handle parsing/casting those bytes.
For instance, given a brewer:
client.addBrewer('tick', (b) => {
let id, count = 0;
(function tick() {
id = setTimeout(tick, 1000);
b.brewJSON({ count });
})();
return () => { clearTimeout(id); };
});
Then on the patron side, it would look like this;
client.addPatron('tick', (p) => {
const handler = (bytes) => {
const text = new TextDecoder().decode(bytes);
const value = JSON.parse(text);
console.log(value.count);
};
p.on('message', handler);
return () => { p.off('message', handler); };
});
NOTE This will be resolved in future versions of the protocol.
Brewers and Patrons will be notified when thier couterparts are added or removed:
client.addBrewer('topic.information', (brewer) => {
const handleAdded = ({ id, topic }) => {
console.log('patron (%s) added', id);
};
const handleRemoved = ({ id }) => {
console.log('patron (%s) removed', id);
};
// setup
brewer.on('added', handleAdded);
brewer.on('removed', handleRemoved);
return function teardown() {
// cleanup
brewer.off('added', handleAdded);
brewer.off('removed', handleRemoved);
};
});
PubkeeperClient ClassThe PubkeeperClient manages the connection to a pubkeeper server.
new PubkeeperClient(options)
| Name | Type | Description |
|---|---|---|
options | ClientOptions | The options for this pubkeeper client |
ClientOptions : objectThese options can be given to a PubkeeperClient to customize its behavior.
| Name | Type | Default | Description |
|---|---|---|---|
server | string | - | Websocket URL of the pubkeeper server |
jwt | string | - | Auth token for the pubkeeper server |
brews | Array<Brew> | [] | Supported brews of this client |
crypto | Map<*,Crypto> | new Map | Supported crypto mappings for this client |
protocols | Map<*,Protocol> | new Map | Supported protocol mappings for this client. Defaults to the legacy protocol |
maxRetries | number | Infinity | Maximum number of connection retries |
maxTimeout | number | 10000 | Maximum timeout between retries (in milliseconds) |
Using ES2015 Promise:
const client = new PubkeeperClient(config)
client.connect().then(() => {
console.log('ready');
});
Using ES2017 async/await:
(async () => {
const client = new PubkeeperClient(config)
await client.connect();
console.log('ready');
})();
.status : stringReturns the status of this client. Can be one of the following:
| Value | Description |
|---|---|
'connecting' | Attempting to connect. |
'connected' | Connected |
'reconnecting' | Has lost the connection, and has queued a reconnection attempt. |
'disconnected' | Disconnected |
'dead' | Given up retrying to connect |
.connect({ wait = true } = {}) : Promise?Connect to the configured pubkeeper server. If { wait: true } is specified, this function will return a Promise that will be resolved when the connection to the pubkeeper server has been finalized. Passing { wait: false } will immediately return undefined and the server may or may not be connected yet.
.disconnect()Disconnect from the server.
.addBrewer(topic[,[ options,] matched]) : BrewerCreate a new brewer of topic.
| Name | Type | Description |
|---|---|---|
topic | string | The topic |
options | BrewerOptions | |
matched | Function | Callback that is invoked when the brewer has been matched with a patron |
BrewerOptions : objectThese options can be given to the PubkeeperClient#addBrewer method to customize the brewers behavior.
| Name | Type | Default | Description |
|---|---|---|---|
autoRemoveListeners | boolean | false | Automatically remove all event listeners on unmatch |
brews | Array<(string|Brew)> | - | A subset of brews to use for this brewer, defaults to all registered brews. |
id | string | - | The UUID of this brewer, defaults to a random UUIDv4 |
waitForBrews | (number|boolean) | 250 | The maximum wait time to wait when brewing data for brew connections to stablize. |
Using the matched callback:
client.addBrewer('topic', (brewer) => {
brewer.brewText('somebody is out there!');
});
With extended options:
// Do not wait for brew connections to stablize before sending.
client.addBrewer(
'topic',
{ waitForBrews: false },
(brewer) => {
brewer.brewText('somebody is out there!');
},
);
Managing resources with teardown function:
client.addBrewer(
'device.motion',
(brewer) => {
const handler = ({ acceleration }) => {
brewer.brewJSON(acceleration);
};
window.addEventListener('devicemotion', handler);
return () => {
window.removeEventListener('devicemotion', handler);
};
},
);
.removeBrewer(brewer)Remove a brewer from this client.
const ref = client.addBrewer('topic', matchedFn);
/* ...later */
client.removeBrewer(ref);
.addPatron(topic[,[ options,] matched]) : PatronCreate a new patron of topic.
| Name | Type | Description |
|---|---|---|
topic | string | The topic |
options | PatronOptions | |
matched | Function | Callback that is invoked when the patron has been matched with a brewer |
PatronOptions : objectThese options can be given to the PubkeeperClient#addPatron method to customize the patrons behavior.
| Name | Type | Default | Description |
|---|---|---|---|
autoRemoveListeners | boolean | false | Automatically remove all event listeners on unmatch |
brews | Array<(string|Brew)> | - | A subset of brews to use for this patron, defaults to all registered brews. |
id | string | - | The UUID of this patron, defaults to a random UUIDv4 |
Using the matched callback:
client.addBrewer('topic', (patron) => {
const handler = (data, meta) => { /* process data */ };
patron.on('message', hanlder);
return () => { patron.off('message', handler); };
});
.removePatron(brewer)Remove a patron from this client.
const ref = client.addPatron('topic', matchedFn);
/* ...later */
client.removePatron(ref);
Brewer ClassA brewer is a publisher of information in a pubkeeper system.
.id : stringthe uuid of this brewer
.brews : Array<string>?the subset of brew names that this brewer is using.
.topic : stringthe topic this brewer is bound to
.count : numbercurrent count of matched patrons
.isMatched : booleanIs the brewer currently matched with any patrons
.brew(bytes[, options]) : PromisePublish binary data to a brewer.
| Name | Type | Description |
|---|---|---|
bytes | (Uint8Array|Uint8ClampedArray) | Binary data to brew |
options | object | |
options.waitForBrews | number? | Override the brewers config |
.brewText(text[, options]) : PromisePublish text as a UTF-8 encoded byte array.
| Name | Type | Description |
|---|---|---|
text | string | text to brew |
options | object | |
options.waitForBrews | number? | Override the brewers config |
.brewJSON(value[, options]) : PromisePublish value using JSON.stringify.
| Name | Type | Description |
|---|---|---|
value | * | value to serialize and brew |
options | object | |
options.waitForBrews | number? | Override the brewers config |
.on(event, fn[, context]) : this.addListener(event, fn[, context]) : thisAdd a listener for a given event.
.off(event, fn[, context[, once]]) : this.removeListener(event, fn[, context[, once]]) : thisRemove the listeners of a given event.
addedFired when a matched patron is added.
| Name | Type | Description |
|---|---|---|
e | object | |
e.id | string | UUID of the added patron |
e.topic | string | topic of the brewer |
removedFired when a matched patron is removed.
| Name | Type | Description |
|---|---|---|
e | object | |
e.id | string | UUID of the removed patron |
Patron ClassA patron is a subscriber of information in a pubkeeper system.
.id : stringthe uuid of this patron
.brews : Array<string>?the subset of brew names that this patron is using.
.topic : stringthe topic this patron is bound to
.count : numbercurrent count of matched brewers
.isMatched : booleanIs the patron currently matched with any brewers
.on(event, fn[, context]) : this.addListener(event, fn[, context]) : thisAdd a listener for a given event.
.off(event, fn[, context[, once]]) : this.removeListener(event, fn[, context[, once]]) : thisRemove the listeners of a given event.
addedFired when a matched brewer is added.
| Name | Type | Description |
|---|---|---|
e | object | |
e.id | string | UUID of the added brewer |
e.topic | string | topic of the brewer |
removedFired when a matched brewer is removed.
| Name | Type | Description |
|---|---|---|
e | object | |
e.id | string | UUID of the removed brewer |
messageFired when a message is recieved from a brewer.
| Name | Type | Description |
|---|---|---|
data | Uint8Array | The binary data from the brewer |
meta | object | |
meta.topic | string | topic of the brewer that brewed this data |
meta.from | string | UUID of the brewer that brewed this data |
FAQs
Pubkeeper javascript SDK
The npm package @pubkeeper/client receives a total of 16 weekly downloads. As such, @pubkeeper/client popularity was classified as not popular.
We found that @pubkeeper/client demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
Malicious Namastex.ai npm packages appear to replicate TeamPCP-style Canister Worm tradecraft, including exfiltration and self-propagation.

Product
Explore exportable charts for vulnerabilities, dependencies, and usage with Reports, Socket’s new extensible reporting framework.

Product
Socket for Jira lets teams turn alerts into Jira tickets with manual creation, automated ticketing rules, and two-way sync.