0.0.3 • Published 2 years ago

@plebbit/plebbit-js v0.0.3

Weekly downloads
-
License
GPL-2.0-only
Repository
github
Last release
2 years ago

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:

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

NameTypeDescription
optionsobjectOptions for the plebbit instance
Options

An object which may have the following keys:

NameTypeDefaultDescription
ipfsGatewayUrlstring'https://cloudflare-ipfs/ipfs/'URL of an IPFS gateway
ipfsApiUrlstring'http://localhost:8080'URL of an IPFS API

Returns

TypeDescription
PlebbitA 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

NameTypeDescription
commentCidstringthe IPFS CID of the comment

Returns

TypeDescription
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

NameTypeDescription
subplebbitIpnsNamestringthe IPNS name of the subplebbit

Returns

TypeDescription
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

NameTypeDescription
commentCommentthe comment to publish
Comment

An object which may have the following keys:

NameTypeDescription
subplebbitIpnsNamestringIPNS name of the subplebbit
postCidstring or nullThe post CID, null if comment is a post
parentCommentCidstring or nullThe parent comment CID, null if comment is a post, same as postCid if comment is top level
contentstringContent of the comment
timestampnumber or nullTime of publishing in ms, Date.now() if null
authorAuthorAuthor of the comment

Returns

TypeDescription
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

NameTypeDescription
voteVotethe vote to publish
Vote

An object which may have the following keys:

NameTypeDescription
subplebbitIpnsNamestringIPNS name of the subplebbit
commentCidstringThe comment or post to vote on
timestampnumber or nullTime of publishing in ms, Date.now() if null
authorAuthorAuthor of the comment, will be needed for voting with NFTs or tokens
vote1 or 0 or -10 is for resetting a vote

Returns

TypeDescription
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

NameTypeDescription
optionsobjectOptions for the subplebbit instance
Options

An object which may have the following keys:

NameTypeDefaultDescription
subplebbitIpnsNamestringundefinedIPNS name of the subplebbit
ipfsGatewayUrlstring'https://cloudflare-ipfs/ipfs/'URL of an IPFS gateway
ipfsApiUrlstring'http://localhost:8080'URL of an IPFS API

Returns

TypeDescription
SubplebbitA 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

NameTypeDescription
subplebbitSubplebbitthe content of the subplebbit
Subplebbit

An object which may have the following keys:

NameTypeDescription
titlestringtitle of the subplebbit
descriptionstringdescription of the subplebbit
moderatorsIpnsNamesstring[]IPNS names of the moderators
latestPostCidstringthe most recent post in the linked list of posts
preloadedPostsPost[]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
pubsubTopicstringthe string to publish to in the pubsub, a public key of the subplebbit owner's choice

Returns

TypeDescription
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

TypeDescription
PostThe 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()