draft
mandatory
Posts and replies are both considered plebbit "comments". They are signed by a plebbit author and published to a subplebbit.
For example:
{
"title": "Why did the banana go to the doctor?",
"content": "It wasn't peeling well.",
"subplebbitAddress": "jokes.eth",
"author": {"address": "john.eth"},
"timestamp": 1728174027,
"signature": {
"signature": <base64>,
"publicKey": <base64>,
"type": "ed25519",
"signedPropertyNames": ["title", "content", "subplebbitAddress", "author", "timestamp"]
}
}
Required fields are:
subplebbitAddress
author
timestamp
signature
Optional fields are:
title
content
link
parentCid
Reserved subplebbit fields:
depth
previousCid
postCid
Comments can be fetched by their IPFS CID (content ID), for example by going to https://ipfsgateway.xyz/ipfs/ or https://ipfsgateway.xyz/ipfs/QmTFuY58c2w9WofMM9KshjUqNncX7EwDK7pV9TMf7aeg1G
Unsigned fields (not included in comment.signature.signedPropertyNames
) must be ignored since they could be maliciously added by someone other than the author.
Fields included in comment.signature.signedPropertyNames
must not be undefined
or null
so signature is easier to verify with CBOR.
Subplebbits may add some unsigned fields like comment.depth
, comment.previousCid
, comment.postCid
, etc. these fields must be ignored until validated through commentUpdate.signature
and commentUpdate.cid
.
Reserved subplebbit fields must not be signed by the comment author or they would cause a collision when the subplebbit tries to overwrite them. The comment author signature would become invalid.
The comment.signature.type
"ed25519"
algorithm is:
const propertiesToSign = {}
for (const propertyName of comment.signature.signedPropertyNames) {
if (comment[propertyName] === undefined || comment[propertyName] === null) {
throw Error('invalid signature')
}
propertiesToSign[propertyName] = comment[propertyName]
}
ed25519.verify(
base64.decode(comment.signature.signature), // signature
cbor.encode(propertiesToSign), // CBOR encoded data to sign
base64.decode(comment.signature.publicKey) // public key
)
A post is like a tweet, a blog post, a reddit post, a facebook post, etc. It may contain comment.title
and/or comment.content
. It should not contain comment.parentCid
.
For example:
{
"title": "Why did the banana go to the doctor?",
"content": "It wasn't peeling well.",
"subplebbitAddress": "jokes.eth",
"author": {"address": "john.eth"},
"timestamp": 1728174027,
"signature": {
"signature": <base64>,
"publicKey": <base64>,
"type": "ed25519",
"signedPropertyNames": ["title", "content", "subplebbitAddress", "author", "timestamp"]
}
}
A link post should contain post.link
, which should be an HTTPS link. It may contain comment.title
and/or comment.content
.
For example:
{
"title": "Breaking: The Sky Is Falling!",
"link": "https://cnn.com/the-sky-is-falling.html",
"subplebbitAddress": "news.eth",
"author": {"address": "john.eth"},
"timestamp": 1728174027,
"signature": {
"signature": <base64>,
"publicKey": <base64>,
"type": "ed25519",
"signedPropertyNames": ["title", "link", "subplebbitAddress", "author", "timestamp"]
}
}
A media post should contain post.link
, which should be an HTTPS link to an image file or video file. It may contain comment.title
and/or comment.content
.
For example:
{
"title": "This is funny!",
"link": "https://funny.com/funny-meme.png",
"subplebbitAddress": "memes.eth",
"author": {"address": "john.eth"},
"timestamp": 1728174027,
"signature": {
"signature": <base64>,
"publicKey": <base64>,
"type": "ed25519",
"signedPropertyNames": ["title", "link", "subplebbitAddress", "author", "timestamp"]
}
}
A reply to a post. It should contain comment.parentCid
. It may contain comment.title
and/or comment.content
.
For example:
{
"parentCid": "QmXnEICVkZBHKgjtj7Vt63HWq3ZfPjcGTSPs79oXtfEZxc",
"content": "Very funny.",
"subplebbitAddress": "jokes.eth",
"author": {"address": "john.eth"},
"timestamp": 1728174027,
"signature": {
"signature": <base64>,
"publicKey": <base64>,
"type": "ed25519",
"signedPropertyNames": ["parentCid", "content", "subplebbitAddress", "author", "timestamp"]
}
}