On this page
Abstract
A community edit lets a holder of the owner or admin role change a community’s public
properties without access to the node that hosts the community. The role holder signs a
communityEdit publication and sends it through the challenge exchange (BSIP-4). The
hosting node authorizes the signer against the community’s roles (BSIP-3), merges
the edit into the community’s current properties, and publishes the result in its next signed
community record. This BSIP defines the publication, which properties each role may change, how the
node validates and applies an edit, and the identity rules that govern a change of the community’s
name.
Motivation
The community record is signed by the community’s key, which lives on the hosting node. The people who run a community (its owners and admins) often do not operate that node, and should not need its private key to update a title, adjust rules, or appoint moderators. A signed publication that the node verifies and applies gives them that ability while keeping the node’s private settings, such as challenge secrets, out of reach. Independent clients and nodes must agree on which fields such a publication may touch and how it is merged, or an edit accepted by one node would mean something else on 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
- A role holder signs a
communityEditand publishes it to the community under thecommunityEditkey of an encryptedCHALLENGEREQUEST(BSIP-4). - The hosting node applies the common acceptance rules (BSIP-4, Publication acceptance), checks the signer’s role, and validates the edit. A community edit targets no comment, so BSIP-4’s checks on a targeted comment do not apply.
- The node merges the edit into its current properties and answers with a
CHALLENGEVERIFICATION. - The node’s next community record carries the new values.
Fields
A communityEdit publication is a JSON object with:
communityEdit— REQUIRED. An object of the properties to change, each OPTIONAL. Only these keys are allowed; the roles that may set each one are given in Authorization:title— string.description— string.rules— array of strings.flairs— object mapping a flair scope (for examplepostorauthor) to an array of flair objects.features— object of feature toggles (BSIP-3, Features).suggested— object of presentation hints (BSIP-3).pubsubTopic— non-empty string, the topic publishers use to reach the community (BSIP-4).roles— object mapping role keys (key addresses or names, BSIP-10) to role objects. A role object has a REQUIRED non-emptyrolestring; other keys are allowed.name— the community’s name (BSIP-10, Names; see Renaming).address— legacy alias ofname(see Renaming).
and the common publication fields: timestamp,
protocolVersion, and signature (REQUIRED), author (OPTIONAL), and communityPublicKey and
communityName (BSIP-2, Community addressing).
The publication is signed by the role holder following
BSIP-2, Signatures. Every top-level field other than signature MUST be
listed in signedPropertyNames. pkc-js lists the present fields in this order: author,
communityPublicKey, communityName, protocolVersion, timestamp, communityEdit. Verifiers
MUST NOT depend on the order.
Example community edit, signed by the key that john.bso resolves to, where the community record’s
roles contains "john.bso": { "role": "owner" }:
{
"communityEdit": {
"description": "Jokes only. No reposts.",
"features": { "noVideos": true },
"roles": { "alice.bso": { "role": "moderator" } }
},
"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", "communityEdit"]
}
}
Authorization
Roles are matched as in BSIP-8, Role matching. A node MUST reject a
communityEdit unless:
- its signer holds the
owneroradminrole; and - if
communityEditcontainsroles,name, oraddress, its signer holds theownerrole.
So title, description, rules, flairs, features, suggested, and pubsubTopic may be
changed by owners and admins, while roles, name, and address may be changed only by owners.
The moderator role cannot publish community edits. Authority comes only from roles: the key that
signs the community record has no implicit role, and an owner role entry grants no access to the
node’s private key or private settings.
Validation
A node MUST reject a communityEdit that fails the common acceptance rules of
BSIP-4, and MUST also reject it when:
- It carries a reserved top-level field:
challengeAnswers,challengeCommentCids,shortCommunityAddress,communityAddress,state,publishingState,signer,clients, ornameResolved. - The signer is not authorized, under Authorization.
communityEditcontains any key not listed in Fields. This covers every other community record field, such aschallenges,encryption,posts,modQueue,postUpdates,statsCid,createdAt,updatedAt, andprotocolVersion, and the node’s privatesettings. Challenge configuration and other private settings can be changed only on the node itself.- The node has already accepted a
communityEditwith the same signature. - The merged result is invalid (see Applying an edit): it does not satisfy the community record format of BSIP-3, a name-form role key cannot be resolved, or a name rule of Renaming fails.
A rejected edit changes nothing. The node answers it with challengeSuccess: false and a reason,
and answers an applied edit with challengeSuccess: true and no encrypted payload.
Applying an edit
For each key K in communityEdit, the node merges the edit’s value into the community’s current
value of K and then applies all merged values at once:
- Where both values are objects, they are merged key by key, recursively.
- Where both values are arrays, they are merged element by element: element
iof the edit is merged into elementiof the current array, and current elements beyond the edit’s length are kept. - Otherwise the edit’s value replaces the current one.
Consequences that publishers MUST account for:
- To add or change one role, feature, suggestion, or flair scope, the edit carries only that entry; the other entries are kept.
- An entry cannot be removed over pubsub. JSON cannot carry
undefined, andnullis not a valid value for any editable property. To take away a role’s authority, an owner sets it to a role string that grants nothing (see BSIP-8, Role matching); removing the entry entirely requires the node operator. - An array such as
rulescannot be shortened over pubsub: an edit can overwrite leading elements, but trailing elements of the current array remain.
When the merged roles contains name-form role keys, the node MUST resolve each of them without
using a cached result and MUST reject the edit if any cannot be resolved. pkc-js checks every
name-form key in the merged map, so an existing role key whose name no longer resolves blocks any
roles edit until the operator removes it.
Renaming
A community’s address is its name when it has one, otherwise its identity key
(BSIP-10, Terminology). Changing the name and changing the address are
therefore one operation, and address is accepted as a legacy alias of name:
{ "name": M }or{ "address": M }, whereMis a name, sets the name toM.{ "address": K }, whereKis the community’s identity key, drops the name.- An edit that carries both
nameandaddressnaming different addresses MUST be rejected, and none of its other properties applied. - The name cannot be dropped with
nameover pubsub, because JSON cannot carry an absent value; an owner drops it withaddressset to the identity key.
The new name MUST be a name under BSIP-10, Names, a string containing a .,
and MUST NOT contain uppercase letters, so that one name is never spelled two ways on the wire. A
node MUST reject an address that is neither such a name nor its own identity key. The node SHOULD
verify that M resolves to its identity key before applying the rename; pkc-js applies the rename
and reports a failed or mismatching resolution only as a local error. Readers verify the name
of every record they load (BSIP-10), so an unverified
name is never shown as the community’s identity.
After a rename from N to M:
- The node’s next record carries
"name": M, and every new publication to the community MUST carrycommunityNameexactlyM(BSIP-10, Community names on publications). - Which stored comments the community keeps in its pages, reply counts, and statistics follows
BSIP-10, Community membership of comments,
evaluated against the new name. Comments signed with
communityNameNstop being members, and become members again if the community is renamed back toN; comments withoutcommunityName, or signed with a spelling ofM, stay members. pkc-js applies these rules every time it builds pages, so a renamed community never publishes a page that clients reject. - Readers do not follow renames: a reader that loaded the community by
Nrejects the record namedMand has to load the community again byMor by its key (BSIP-10). A reader that loaded it by key keeps following it. - Comments, votes, edits, and moderations accepted before the rename keep the
communityNamethey were signed with; signed fields never change.
Publishing the result
Once an edit is applied, the node MUST include the new values in its next community record
(BSIP-3). That record has a later updatedAt and a new signature by the community
key over every field it carries. Readers see the edit only in that record.
The CHALLENGEVERIFICATION for an applied edit is sent once the node’s state has changed, which can
be before the new record is published. A publisher that needs to see the result SHOULD wait for a
record whose updatedAt is later than its edit’s timestamp.
A change to pubsubTopic moves the community’s challenge exchange to the new topic. Publishers use
the topic of the latest record they have verified. A change to features applies to publications
the node validates after the edit, and does not alter comments already stored.
Rationale
- Owner and admin tiers. Most edits are routine and can be delegated to admins.
rolesand the name decide who controls the community and what it is called, so they stay with owners. - Public properties only. Everything a community edit can touch is already public in the community record. Challenge secrets, allowlists, and other private settings never travel over pubsub, so a compromised role key cannot read or replace them.
- Merging instead of replacing lets an edit carry only what changes, so two admins editing
different entries of
rolesorfeaturesdo not erase each other’s work. The element-wise rule for arrays mirrors the current implementation; a later revision may make arrays replace wholesale. - A rename is an identity change. Authors sign
communityNameinto every publication. A comment signed forNwas published to the community calledN, so a community now calledMdoes not present it as its own. Applying the same membership rules on the node as clients apply to pages means a node never publishes a page that clients reject and never hides a comment clients would accept. - One operation for name and address. Treating
nameand the legacyaddressas one value, and rejecting an edit where they disagree, leaves no state in which a node is addressed by one name and signs records carrying another.
Backwards Compatibility
addressis accepted incommunityEditas a legacy alias ofname, after theaddressfield that older community records carried (BSIP-10). A node rewrites it intonameas described in Renaming; it never appears in a new community record.- Comments stored before publications carried
communityPublicKeyandcommunityNameseparately may have neither field. BSIP-10’s membership rules keep them through any rename, because they accept a missing field. - A node that does not implement community edits rejects them; role holders can still edit through the node operator.
Security Considerations
- Authorization bypass. Community edits are authorized only through
BSIP-8, Role matching. Accepting a claimed
author.namewithout resolving it, or matching a runtime author address, would let anyone rewrite the community. - Privilege escalation. Owners can grant the
ownerrole to any key. Admins cannot touchrolesor the name, but they can changepubsubTopic, which moves the challenge exchange, andfeatures, which includespseudonymityModeand other privacy- and access-relevant switches. Communities SHOULD grantadminonly to keys trusted with those powers. - Revocation. Because entries cannot be removed over pubsub, a compromised role key can be neutralized by an owner but only removed by the node operator. A compromised owner key can likewise neutralize the other owners; the node operator remains the final authority, through edits made on the node itself.
- Replay. A community edit can be replayed within the timestamp window, and a node that applied
it twice could undo a later edit, for example restoring an earlier title. Nodes MUST reject a
communityEditwhose signature they have already accepted (rule 4 of Validation); pkc-js does not yet do this for community edits. The signedcommunityPublicKeyandcommunityNamestop an edit from being replayed to another community. - Name hijacking. An owner can rename the community to a name that does not resolve to it. Readers verify names before trusting them (BSIP-10), and nodes SHOULD refuse such renames.
- Stale authorization. Roles are checked when the request is validated, before any interactive challenge; see the re-check recommended in BSIP-8, Role matching.
- Abuse. Every applied edit forces a new community record. Community edits pass through the community’s challenges and the publication size limit of BSIP-4; communities SHOULD rate-limit them.
- Surprising merges. Publishers that expect replacement semantics can leave stale roles or array elements in place. Clients SHOULD show the role holder the merged result, read from the next record, rather than the edit they sent.
Copyright
Copyright and related rights waived via CC0.