npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@plebbit/plebbit-js

v0.0.3

Published

plebbit.js will be an NPM module to wrap around the IPFS APIs used by Plebbit. It will be used in all clients: CLI, Electron (Desktop GUI) and Web.

Downloads

192

Readme

Telegram group for this repo https://t.me/plebbitjs

plebbit-js will be an NPM module to wrap around the IPFS APIs used by Plebbit. It will be used in all clients: CLI, Electron (Desktop GUI) and Web.

Glossary:

  • CID: https://docs.ipfs.io/concepts/content-addressing/
  • IPNS: https://docs.ipfs.io/concepts/ipns/#example-ipns-setup-with-cli
  • IPNS name: hash of a public key, the private key is used by subplebbit owners for signing IPNS records, and by authors for signing posts and comments
  • Pubsub topic: the string to publish/subscribe to in the pubsub https://github.com/ipfs/js-ipfs/blob/master/docs/core-api/PUBSUB.md#ipfspubsubsubscribetopic-handler-options and https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.0.md#topic-membership
  • IPNS record: https://github.com/ipfs/specs/blob/master/IPNS.md#ipns-record
  • IPNS signature: https://github.com/ipfs/notes/issues/249
  • Examples of how to sign: https://github.com/plebbit/whitepaper/blob/main/signature-examples/sign.js

Note: IPFS files are immutable, fetched by their CID, which is a hash of their content. IPNS records are mutable, fetched by their IPNS name, which is the hash of a public key. The private key's owner can update the content. Always use IPFS files over IPNS records when possible because they are much faster to fetch.

Schema:

Publication: {
  author: Author,
  timestamp: number,
  signature: Signature // sign immutable fields like author, title, content, timestamp to prevent tampering
}
Comment (IPFS file): {
  ...Publication,
  subplebbitIpnsKeyId: string, // required to prevent malicious subplebbits republishing as original and helps faster loading subplebbit info for comment direct linking
  postCid: string, // helps faster loading post info for comment direct linking
  parentCommentCid: string, // same as postCid for top level comments
  content: string,
  previousCommentCid: string, // each post is a linked list
  commentIpnsKeyId: string // each post/comment needs its own IPNS record (CommentIpns) for its mutable data like edits, vote counts, comments
}
Post (IPFS file): {
  ...Comment,
  parentCommentCid: null, // post is same as comment but has no parent and some extra fields,
  title: string
}
Vote {
  ...Publication,
  commentCid: string,
  vote: 1 | -1 | 0 // 0 is needed to cancel a vote
}
CommentIpns (IPNS record): {
  latestCommentCid: string, // the most recent comment in the linked list of posts
  preloadedComments: Comment[], // preloaded content greatly improves loading speed, it saves scrolling the entire linked list, should include preloaded nested comments and vote counts
  upvoteCount: number,
  downvoteCount: number
}
Author {
  displayName: string,
  ipnsKeyId: string
}
Signature {
  signature: string, // data in base64
  publicKey: buffer, // include public key (marshalled, like IPNS does it) because the IPNS name is just a hash of it
  type: string // multiple versions/types to allow signing with metamask/other wallet or to change the signature fields or algorithm
}
Subplebbit (IPNS record): {
  title: string,
  description: string,
  moderatorsIpnsNames: string[],
  latestPostCid: string, // the most recent post in the linked list of posts
  preloadedPosts: Post[], // preloaded content greatly improves loading speed, it saves scrolling the entire linked list, should include some preloaded comments for each post as well and vote counts
  pubsubTopic: string // the string to publish to in the pubsub, a public key of the subplebbit owner's choice
}

Message signature types:

  • 'plebbit1':
const libp2pCrypto = require('libp2p-crypto')
const cborg = require('cborg')
const PeerId = require('peer-id')

const encryptedPemPassword = ''
const rsaInstance = await libp2pCrypto.keys.import(privateKeyPemString, encryptedPemPassword)

const messageToSign = cborg.encode({subplebbitIpnsName, author, title, content, timestamp}) // use cborg to stringify deterministically instead of JSON.stringify
const rsaInstanceSignature = await rsaInstance.sign(messageToSign)

// can also be done in node (but not browser compatible)
require('crypto').sign('sha256', messageToSign, privateKeyPemString)

// to get marshalled (serialized) public key for signature.publicKey field
signature.publicKey = rsaInstance.public.marshal()
// or
signature.publicKey = libp2pCrypto.keys.marshalPublicKey(rsaInstance.public, 'RSA')

// to verify a signed post
const post = {/* ...some post */}
const postToVerify = cborg.encode({subplebbitIpnsName: post.subplebbitIpnsName, author: post.author, title: post.title, content: post.content, timestamp: post.timestamp})
const rsaPublicKeyInstance = (await PeerId.createFromPubKey(post.signature.publicKey)).pubKey
const signatureIsValid = await rsaPublicKeyInstance.verify(postToVerify, post.signature.signature)

Pubsub message types

PubsubMessage: {
  type: 'CHALLENGEREQUEST' | 'CHALLENGE' | 'CHALLENGEANSWER' | 'CHALLENGEVERIFICATION'
}
ChallengeRequestMessage (sent by post author) {
  ...PubsubMessage,
  challengeRequestId: string, // random string choosen by sender
  acceptedChallengeTypes: string[], // list of challenge types the client can do, for example cli clients or old clients won't do all types
  publication: Publication // include the post so the nodes and subplebbit owner can blacklist it outright
}
ChallengeMessage (sent by subplebbit owner) {
  challengeRequestId: string,
  challenge: Challenge
}
ChallengeAnswerMessage (sent by post author) {
  challengeRequestId: string,
  challengeAnswerId: string, // random string choosen by sender
  challengeAnswer: string // for example 2+2=4
}
ChallengeVerificationMessage (sent by subplebbit owner) {
  challengeRequestId: string, // include in verification in case a peer is missing it
  challengeAnswerId: string, // include in verification in case a peer is missing it
  challengeAnswerIsVerified: bool,
  reason: string // reason for failed verification, for example post content is too long. could also be used for successful verification that bypass the challenge, for example because an author has good history
}
Challenge {
  type: 'captcha1', // will be dozens of challenge types, like holding a certain amount of a token
  challenge: buffer // data required to complete the challenge, could be html, png, etc.
}

API

Plebbit API

The plebbit API for reading and writing to and from subplebbits.

Plebbit(options)

Create a plebbit instance.

Parameters

| Name | Type | Description | | ---- | ---- | ----------- | | options | object | Options for the plebbit instance |

Options

An object which may have the following keys:

| Name | Type | Default | Description | | ---- | ---- | ------- | ----------- | | ipfsGatewayUrl | string | 'https://cloudflare-ipfs/ipfs/' | URL of an IPFS gateway | | ipfsApiUrl | string | 'http://localhost:8080' | URL of an IPFS API |

Returns

| Type | Description | | -------- | -------- | | Plebbit | A plebbit instance |

Example

const Plebbit = require('@plebbit/plebbit-js')
const options = {
  ipfsGatewayUrl: 'https://cloudflare-ipfs/ipfs/',
  ipfsApiUrl: 'http://localhost:5001',
}
const plebbit = Plebbit(options) // should be independent instance, not singleton

plebbit.getComment(commentCid)

Get a plebbit comment by its IPFS CID. Posts are also comments.

Parameters

| Name | Type | Description | | ---- | ---- | ----------- | | commentCid | string | the IPFS CID of the comment |

Returns

| Type | Description | | -------- | -------- | | Promise<GetCommentResponse> | A object with comment data |

Object is of the form:

{
  author: Author,
  timestamp: number,
  signature: Signature,
  postCid: string,
  getPost: function, // if comment is a post, it gets itself
  parentCommentCid: string || null, // post don't have parent cids
  subplebbitIpnsName: string,
  getSubplebbit: function,
  title: string || null, // comments don't have titles
  content: string,
  previousCommentCid: string,
  getPreviousComment: function,
  commentIpnsName: string,
  getCommentIpns: function
}

Example

const commentCid = 'QmbWqx...'
const comment = await plebbit.getComment(commentCid)
console.log('comment:', comment)
if (comment.parentCommentCid) { // comment with no parent cid is a post
  comment.getPost(post => console.log('post:', post))
}
comment.getCommentIpns().then(commentIpns => console.log('commentIpns:', commentIpns))
comment.getSubplebbit().then(subplebbit => console.log('subplebbit:', subplebbit))
comment.getPreviousComment().then(previousComment => console.log('previousComment:', previousPost))
/*
Prints:
{ ...TODO }
*/

plebbit.getSubplebbit(subplebbitIpnsName)

Get a subplebbit comment by its IPNS name.

Parameters

| Name | Type | Description | | ---- | ---- | ----------- | | subplebbitIpnsName | string | the IPNS name of the subplebbit |

Returns

| Type | Description | | -------- | -------- | | Promise<GetSubplebbitResponse> | A object with subplebbit data |

Object is of the form:

{
  subplebbitIpnsName: string,
  title: string,
  description: string,
  moderatorsIpnsNames: string[],
  latestPostCid: string,
  preloadedPosts: Post[],
  pubsubTopic: string
}

Example

const subplebbitIpnsName = 'QmbWqx...'
const subplebbit = await plebbit.getSubplebbit(subplebbitIpnsName)
console.log(subplebbit)

let currentPostCid = subplebbit.latestPostCid
const scrollAllSubplebbitPosts = async () => {
  while (currentPostCid) {
    const post = await plebbit.getComment(currentPostCid)
    console.log(post)
    currentPostCid = post.previousPostCid
  }
  console.log('there are no more posts')
}
scrollAllSubplebbitPosts()
/*
Prints:
{ ...TODO }
*/

plebbit.publishComment(comment)

Publish a comment on a subplebbit. Posts are also comments.

Parameters

| Name | Type | Description | | ---- | ---- | ----------- | | comment | Comment | the comment to publish |

Comment

An object which may have the following keys:

| Name | Type | Description | | ---- | ---- | ----------- | | subplebbitIpnsName | string | IPNS name of the subplebbit | | postCid | string or null | The post CID, null if comment is a post | | parentCommentCid | string or null | The parent comment CID, null if comment is a post, same as postCid if comment is top level | | content | string | Content of the comment | | timestamp | number or null | Time of publishing in ms, Date.now() if null | | author | Author | Author of the comment |

Returns

| Type | Description | | -------- | -------- | | Promise<PublishCommentResponse> | The publish comment response |

Object is of the form:

{ // ...TODO }

Example

// TODO

plebbit.publishVote(vote)

Publish a vote on a comment or post.

Parameters

| Name | Type | Description | | ---- | ---- | ----------- | | vote | Vote | the vote to publish |

Vote

An object which may have the following keys:

| Name | Type | Description | | ---- | ---- | ----------- | | subplebbitIpnsName | string | IPNS name of the subplebbit | | commentCid | string | The comment or post to vote on | | timestamp | number or null | Time of publishing in ms, Date.now() if null | | author | Author | Author of the comment, will be needed for voting with NFTs or tokens | | vote | 1 or 0 or -1 | 0 is for resetting a vote |

Returns

| Type | Description | | -------- | -------- | | Promise<PublishVoteResponse> | The publish vote response |

Object is of the form:

{ // ...TODO }

Example

// TODO

Subplebbit API

The subplebbit API for creating, updating and running subplebbits.

Subplebbit(options)

Create a subplebbit instance.

Parameters

| Name | Type | Description | | ---- | ---- | ----------- | | options | object | Options for the subplebbit instance |

Options

An object which may have the following keys:

| Name | Type | Default | Description | | ---- | ---- | ------- | ----------- | | subplebbitIpnsName | string | undefined | IPNS name of the subplebbit | | ipfsGatewayUrl | string | 'https://cloudflare-ipfs/ipfs/' | URL of an IPFS gateway | | ipfsApiUrl | string | 'http://localhost:8080' | URL of an IPFS API |

Returns

| Type | Description | | -------- | -------- | | Subplebbit | A subplebbit instance |

Example

const {Subplebbit} = require('@plebbit/plebbit-js')
const options = {
  ipfsGatewayUrl: 'https://cloudflare-ipfs/ipfs/',
  ipfsApiUrl: 'http://localhost:5001',
  subplebbitIpnsName: 'Qmb...'
}
const subplebbit = Subplebbit(options) // should be independent instance, not singleton
subplebbit.update({
  title: 'Memes',
  description: 'Post your memes here.',
  pubsubTopic: 'Qmb...'
})
subplebbit.on('post', (post) => console.log(post))
subplebbit.start()

subplebbit.update(subplebbit)

Update the content of a subplebbit.

Parameters

| Name | Type | Description | | ---- | ---- | ----------- | | subplebbit | Subplebbit | the content of the subplebbit |

Subplebbit

An object which may have the following keys:

| Name | Type | Description | | ---- | ---- | ----------- | | title | string | title of the subplebbit | | description | string | description of the subplebbit | | moderatorsIpnsNames | string[] | IPNS names of the moderators | | latestPostCid | string | the most recent post in the linked list of posts | | preloadedPosts | Post[] | preloaded content greatly improves loading speed, it saves scrolling the entire linked list, should include some preloaded comments for each post as well and vote counts | | pubsubTopic | string | the string to publish to in the pubsub, a public key of the subplebbit owner's choice |

Returns

| Type | Description | | -------- | -------- | | Promise<SubplebbitUpdateResponse> | The update subplebbit response |

Object is of the form:

{ // ...TODO }

Example

// TODO

subplebbit.start()

Start listening for new posts on the pubsub, and publishing them every 5 minutes.

Example

const options = {
  ipfsGatewayUrl: 'https://cloudflare-ipfs/ipfs/',
  ipfsApiUrl: 'http://localhost:5001',
  subplebbitIpnsName: 'Qmb...'
}
const subplebbit = Subplebbit(options)
subplebbit.on('post', (post) => console.log(post))
subplebbit.start()

subplebbit.stop()

Stop listening for new posts on the pubsub, and stop publishing them every 5 minutes.

Subplebbit Events

The subplebbit events.

post

A new post is published.

Emits

| Type | Description | | -------- | -------- | | Post | The published post |

Object is of the form:

{ // ...TODO }

Example

const options = {
  ipfsGatewayUrl: 'https://cloudflare-ipfs/ipfs/',
  ipfsApiUrl: 'http://localhost:5001',
  subplebbitIpnsName: 'Qmb...'
}
const subplebbit = Subplebbit(options)
subplebbit.on('post', (post) => console.log(post))
subplebbit.start()