On this page
Abstract
A comment edit is a publication an author signs to change or delete one of their own comments
after a community has accepted it. The comment record is immutable (BSIP-2), so an
edit never modifies it. Instead the community validates the edit, keeps it, and embeds the author’s
most recently accepted edit, unmodified and still author-signed, in the comment’s update
(BSIP-5). This BSIP defines the commentEdit publication, who may publish it, how a
community validates and applies it, what deletion means, and how clients present and verify the
result.
Motivation
Authors need to fix mistakes, add spoiler or NSFW warnings after the fact, change their post flairs, and withdraw what they wrote. A content-addressed comment cannot be changed in place. Clients, community nodes, and readers therefore have to agree on a separate edit object, on the rule that only the comment’s author may publish one, and on how the edit travels inside the community-signed comment update. Without that agreement, an edit made in one client is invisible or unverifiable in another.
Specification
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119 and RFC 8174.
Overview
- The author signs a
commentEditthat names the target comment bycommentCid, and publishes it to the comment’s community under thecommentEditkey of an encryptedCHALLENGEREQUEST(BSIP-4). - The community applies the common acceptance rules
(BSIP-4, Publication acceptance) and the edit rules in this
BSIP, and answers with a
CHALLENGEVERIFICATION. - The community stores the accepted edit. The author’s most recently accepted edit becomes the
editfield of the comment’s next comment update. - Clients verify
editagainst the comment record and merge it into the comment they display.
Fields
A commentEdit is a JSON object with these edit fields:
commentCid— REQUIRED. String, the CID of the comment being edited.content— OPTIONAL. String, replacement body text (Markdown).deleted— OPTIONAL. Boolean.truedeletes the comment (see Deletion).flairs— OPTIONAL. Array of flair objects, the author’s replacement post flairs.spoiler— OPTIONAL. Boolean.nsfw— OPTIONAL. Boolean.reason— OPTIONAL. String, the author’s note explaining the edit.
and the common publication fields: timestamp,
protocolVersion, and signature (REQUIRED), author (OPTIONAL), and the community addressing
fields communityPublicKey and communityName
(BSIP-2, Community addressing).
An edit can change only the edit fields above. title, link, gallery, parentCid, and every
other field of the comment record stay as the author first signed them.
Example edit as signed by the author:
{
"commentCid": "QmbKFFGL9EMwdMVrkJUqz2yQAorzUBExchK1qogsU8BJ7e",
"content": "It wasn't peeling well. (Edit: credit to my nephew for this one.)",
"spoiler": true,
"reason": "Added credit",
"communityPublicKey": "12D3KooWBexQF4qDvyaxDgK4r2qPNk5z8s9b1eU5z2bnR6dExamPLe",
"communityName": "jokes.bso",
"author": { "name": "john.bso" },
"timestamp": 1792764000,
"protocolVersion": "1.0.0",
"signature": {
"type": "ed25519",
"signature": "<base64>",
"publicKey": "<base64, 32 bytes>",
"signedPropertyNames": ["author", "communityPublicKey", "communityName", "protocolVersion", "timestamp", "commentCid", "content", "spoiler", "reason"]
}
}
Signing
An edit is signed by the author following BSIP-2, Signatures. Every
top-level field other than signature MUST be listed in signedPropertyNames; a community rejects
an edit that carries an unsigned field. The current implementation, pkc-js, lists the fields that
are present in this order: author, communityPublicKey, communityName, protocolVersion,
timestamp, commentCid, content, deleted, flairs, spoiler, nsfw, reason. Verifiers
MUST use the list carried in the signature and MUST NOT depend on its order.
Who may edit
Only the key that signed a comment may edit it:
- If the community stored the comment under its author’s own key, the edit’s
signature.publicKeyMUST equal the comment record’ssignature.publicKey. - If the community stored the comment under a pseudonymity alias
(BSIP-2, Pseudonymity), the edit’s
signature.publicKeyMUST equal the original author public key the community recorded when it accepted that comment. An edit signed by the alias key MUST be rejected.
A community MUST make this decision by comparing public keys. It MUST NOT authorize an edit because
of author.name, a derived author address, or a role. Owners, admins, and moderators can edit only
the comments they signed; they act on other people’s comments through comment moderation
(BSIP-8).
Validation
A community MUST reject a commentEdit that fails the rules every publication must meet
(BSIP-4, Publication acceptance). Those rules include
BSIP-4’s checks on the targeted comment, here the edited comment. For an edit they mean: the comment
exists and is not removed, deleted, pending, or disapproved (an edit that deletes a pending or
disapproved comment is allowed); its thread is not removed, deleted, or archived, nor locked unless
the signer holds a moderation role; and the edit’s timestamp is not earlier than the comment’s.
In addition, a community MUST reject a commentEdit that:
- is not signed by the comment’s author, under Who may edit;
- has the same signature as an edit the community already accepted. BSIP-4, Duplicate requests governs the answer to a resend;
- breaks a community feature (BSIP-3, Features). The checks the
community applies to comments also apply to the edited values:
noMarkdownImages,noMarkdownVideos,noMarkdownAudio:contentembeds that media type;noSpoilers:spoileristrue;noSpoilerReplies:spoileristrueand the edited comment is a reply;flairsis non-empty and eitherpostFlairsis not enabled or a flair is not deep-equal to an entry of the community record’sflairs.postlist. This applies to replies too;
- carries a reserved top-level field:
insertedAt,authorSignerAddress,isAuthorEdit,extraProps,challengeAnswers,challengeCommentCids,commentEdit,shortCommentCid,shortCommunityAddress,communityAddress,state,publishingState,signer,clients, ornameResolved. These names are kept for node bookkeeping and runtime fields. Other signed fields unknown to the community are accepted and kept with the edit.
A community answers a rejected edit with challengeSuccess: false and a reason, and an accepted
one with challengeSuccess: true and no encrypted payload.
Pseudonymous comments
When the edited comment is stored under a pseudonymity alias:
- The author signs the edit with their own key, and the community authorizes it against the recorded original key, as above.
- Before storing the edit, the community MUST remove its
authorfield and re-sign it with that comment’s alias private key, keeping the other fields defined in this BSIP unchanged. It MAY drop signed fields it does not understand; pkc-js does. - The stored edit, and the one later placed in the comment update, is the alias-signed edit. Its
signature.publicKeyequals the comment record’ssignature.publicKey, so readers verify it like any other edit. Neither the original author’s key nor their author object appears in the comment update.
Because the community holds the alias private key, it can produce such edits on its own; see Security Considerations.
The effective edit
A community keeps every edit it accepts. The effective edit of a comment is the edit signed by
the comment record’s key (the alias key for a pseudonymous comment) that the community accepted
last. Acceptance order decides, not timestamp.
Each edit is a complete statement of the author-side state, not a patch. A field missing from the effective edit is unedited, even if an earlier edit set it. A client that wants to keep an earlier change MUST repeat it in every later edit.
In each comment update it publishes (BSIP-5), the community:
- MUST set
editto the effective edit exactly as signed:signatureand every field listed in itssignedPropertyNames, unmodified, so that it verifies on its own; - MUST omit
editwhen the comment has no effective edit, and MUST NOT put any other object there; - MUST NOT copy the edit’s
reasoninto the comment update’s top-levelreason, which belongs to moderators (BSIP-8); - SHOULD publish a new comment update promptly after accepting an edit.
A community MUST NOT accept an edit it cannot later reproduce byte-for-byte. pkc-js rebuilds the stored edit before committing it and rejects the publication if the rebuilt copy no longer verifies.
Presentation
Given a comment record C, its comment update U, and E = U.edit, a client displays:
- deleted:
E.deleted === true. - content:
E.contentwhen it is a non-empty string, otherwiseC.content. - spoiler:
U.spoilerwhen it is a boolean, otherwiseE.spoilerwhen it is a boolean, otherwiseC.spoiler. nsfw follows the same rule. - flairs:
U.flairswhen present, otherwiseE.flairswhen present, otherwiseC.flairs. - author flairs:
U.author.community.flairswhen present, otherwiseE.author.flairswhen present, otherwiseC.author.flairs. - reason:
U.reasonis the moderator’s reason.E.reasonMUST be presented, if at all, as the author’s edit note and never as a moderation reason. - Every other field, including
titleandlink, fromC.
Values set by moderators therefore take precedence over the author’s edit whichever was published
later. Clients SHOULD indicate that a comment was edited when U.edit is present, and MAY offer the
original text from C.
Verification by clients
In addition to verifying the comment update (BSIP-5), a client that receives
U.edit MUST check that:
- the edit’s signature verifies under BSIP-2, using the edit’s own
signedPropertyNames; - the edit’s
signature.publicKeyequals the comment record’ssignature.publicKey; - the edit’s
commentCidequals the comment update’scid.
If any check fails, the client MUST treat the comment update as invalid.
Deletion
An edit with deleted: true deletes the comment. Deletion is final:
- Once a deletion is accepted, BSIP-4’s checks on the targeted comment (see
Validation) reject every further edit of the comment, including one with
deleted: false, as well as votes and replies on it, and every publication in a deleted post’s thread except moderations. The deletion edit therefore stays the effective edit. - A comment is deleted exactly when its comment update’s
edit.deletedistrue. - A deleted comment is not counted in its ancestors’
replyCount,childCount,lastChildCid, orlastReplyTimestamp(BSIP-5); the present replies under a deleted reply still count. - Page contents are governed by BSIP-3, Pages. By default pkc-js leaves deleted posts out of the community’s posts pages and keeps deleted replies in reply pages, where clients render them as tombstones.
- Deleting a comment that is pending approval or has been disapproved removes it from the community entirely, as a purge would (BSIP-8).
- An author cannot delete a comment a moderator has removed, because those checks reject any edit of a removed comment.
Deletion does not erase content. The comment record stays content-addressed, the community keeps serving it until a moderator purges it, and peers may hold copies. Clients SHOULD NOT display the content, title, or link of a deleted comment.
Ordering, timestamps, and limits
- Beyond the acceptance window of BSIP-4 and the rule that an
edit is not older than its comment, an edit’s
timestamphas no effect. It does not order edits. - When several edits for one comment arrive close together, the one the community accepts last is
the effective edit. Authors SHOULD wait for one edit’s
CHALLENGEVERIFICATIONbefore publishing the next. - There is no protocol limit on how many edits a comment receives or on how long after publication it may be edited, other than the locked and archived states. The publication size limit of BSIP-4 applies to the whole edit. Communities limit edit volume through their anti-spam challenges (BSIP-3).
Rationale
- An author-signed edit inside a community-signed update lets the community attach the edit without being able to forge the author’s words, and lets readers verify it with no extra fetch.
- Snapshots instead of patches keep the comment update to a single, self-contained edit. Readers need no edit history, and a community cannot change the meaning of an edit by dropping an earlier one. The cost is that clients must resend fields they want to keep.
- Acceptance order instead of timestamps. Timestamps are chosen by the author within a window, so two edits can arrive out of timestamp order. The community already decides what the comment update says; letting it order edits avoids a second, conflicting clock.
- Final deletion removes delete and undelete churn, gives tombstones a stable meaning, and lets communities reject activity under deleted content without tracking history.
- Key matching only. Authorship is a property of the signing key. Roles govern a community’s moderation, which BSIP-8 handles with a separate, community-asserted mechanism.
- Re-signing under the alias keeps pseudonymous comments verifiable by readers without revealing which original key wrote them.
Backwards Compatibility
- Comment updates published by older nodes can carry an
editwith the legacysubplebbitAddressfield (the former single community-address field) or anauthor.addressfield, listed in that edit’ssignedPropertyNames. Clients MUST verify such edits with their ownsignedPropertyNamesand SHOULD accept them. Communities reject new edits that carrysubplebbitAddressorcommunityAddress(BSIP-4). - Signed fields a community does not understand are kept and carried in
U.edit, so newer clients can add edit fields without breaking older communities. Clients ignore edit fields they do not understand. Under pseudonymity such fields may be dropped when the community re-signs the edit. - Check 3 of Verification by clients is new in this BSIP. Edits placed by an honest community always satisfy it.
Security Considerations
- Authorization bypass. The only valid authorization is equality between the edit’s signing key
and the key that signed the comment (or the recorded original key under pseudonymity). A community
that matched on
author.name, a runtime author address, or a role would let anyone who claims a name edit or delete other people’s comments. - Forged and transplanted edits. A community cannot forge an edit of a comment signed by its author’s own key. It can choose which accepted edit to show, or withhold edits, within its general authority over comment updates (BSIP-5). Without check 3 of Verification by clients, a community could attach a genuine edit of one comment to another comment by the same author, for example showing a deletion or replacement text on a comment the author never edited. Clients MUST perform that check.
- Pseudonymous comments. The community holds every alias private key, so for a pseudonymous comment it can sign any edit it likes. Readers cannot tell such an edit from one the author asked for; trust in edits of pseudonymous comments is trust in the community.
- Replay. A replayed edit is rejected as a duplicate by signature, and outside the timestamp
window. The signed
communityPublicKey,communityName, andcommentCidstop an edit from being replayed to another community or onto another comment. - Deletion is not erasure. Authors and clients MUST NOT treat deletion as removal of data. Copies of the comment record persist with the community until purged and with any peer that fetched it.
- Bait and switch. An author can collect votes and replies and then replace the content. Clients SHOULD mark edited comments, and communities that care MAY lock or archive posts to freeze them.
- Abuse. Each accepted edit forces the community to republish a comment update. Edits pass through the community’s challenges like any publication, and communities SHOULD rate-limit them.
- Author flairs in edits. Clients use
E.author.flairsas author flairs (see Presentation), but pkc-js does not check those flairs against the community’sauthorFlairsfeature andflairs.authorlist. Communities SHOULD apply to an edit’sauthor.flairsthe same rules they apply to a comment’s author flairs.
Copyright
Copyright and related rights waived via CC0.