Skip to content

Build with PsstPsst

Stable groups

Implement PsstPsst stable groups over NIP-17: h tags for group identity, membership actions, renaming, and group-state validation across membership changes.

Protocol source ↗
On this page

draft optional

This extension gives PsstPsst group conversations a stable identity across membership changes. It builds on NIP-17 and uses the split-key envelope and millisecond timestamp extensions.

Stable group identity

NIP-17 normally identifies a room by the rumor author and its p tags, so a membership change creates another room. PsstPsst adds an opaque stable ID using an h tag, whose naming follows NIP-29:

["h", "<group-id>"]

Only the first h tag is used. Its value is 1–256 UTF-8 bytes. Senders SHOULD generate 32 random bytes and encode them as 64 lowercase hexadecimal characters. A group ID is an identifier, not a public key.

Group kind 14 and kind 15 rumors and wrapped kind 7 reactions carry the same h value. Groups do not introduce a shared encryption key. Each rumor is sealed and gift-wrapped separately for every addressed member and for the sender. Its p tags remain the delivery audience for that rumor.

Group actions

The first action tag describes a group state change:

["action", "invite", "<target-pubkey>"]
["action", "remove", "<target-pubkey>"]
["action", "rename"]

Targets are 64-character lowercase hexadecimal public keys. A malformed first action tag invalidates the rumor; later action tags are ignored.

A complete invite rumor, before sealing and gift wrapping, has this form:

{
  "id": "<rumor-id>",
  "pubkey": "<author-identity-pubkey>",
  "created_at": "<unix-timestamp>",
  "kind": 14,
  "tags": [
    ["p", "<current-member-pubkey>"],
    ["p", "<invited-member-pubkey>"],
    ["h", "<group-id>"],
    ["ms", "<0..999>"],
    ["action", "invite", "<invited-member-pubkey>"],
    ["subject", "<current-group-name>"]
  ],
  "content": ""
}

The rumor is unsigned; its kind 13 seal supplies the identity signature. The subject tag is optional.

Invite

invite is an empty kind 14 rumor. Its target is the new member. Its p tags contain the current members other than the author and MUST include the target. This lets the target initialize the group without earlier history.

Remove and leave

remove is an empty kind 14 rumor. When removing another member, the target MUST remain in p so it can receive the removal. When the target equals the author, the action means leave and the target need not appear in p.

Rename

rename is an empty kind 14 rumor with a subject tag. A non-empty trimmed value sets the group name. A missing, empty, or whitespace-only value clears it. Names contain at most 80 Unicode code points.

Ordinary kind 14 and kind 15 messages SHOULD include the current subject (an empty value for an unnamed group), so another device can recover the name without an earlier rename or initial message. An invite MAY also include it.

Group state

An unknown group is initialized by an ordinary non-empty kind 14 message, a valid kind 15 file offer, or an invite. Its initial roster is the author plus the valid p values. No first-message action or earlier history is required. Reactions, empty ordinary messages, remove, and rename cannot initialize a group. Legacy exact-shape action=create tags are treated as ordinary content, not state changes. After initialization, ordinary-message p tags describe only that rumor’s audience and MUST NOT replace the roster.

Membership actions are applied in rumor order. Their author MUST be a member at that point in the action history. invite adds its target and remove removes its target. A later invite may add a removed member again. Any current member may publish an action; there is no administrator or membership quorum.

Only the first subject tag is used. On any accepted kind 14 or kind 15 group rumor, absence preserves the name, a missing or empty value clears it, and other values are trimmed. The newest accepted subject under the event order wins independently of membership replay. An overlong subject is ignored on an ordinary message or invite; it invalidates a rename action.

This extension does not deliver earlier rumors to a newly invited member.

Private saved groups

Saved-group bookmarks use the shared contacts list described in Private account lists. Saving a group does not change its roster or message encryption.

Compatibility

Stable-group interoperability requires processing h and action in addition to the split-key envelope.