Expand description
Message envelope (GROUP_PROTOCOL.md).
A Community message is an inner Nostr event signed by the author’s real key (the intra-group authorship proof), NIP-44-v2-encrypted under the shared channel key, and wrapped in an ephemeral-signed outer event tagged with the per-epoch pseudonym. Single NIP-44 pass, O(1) broadcast — not gift wrap’s per-recipient double-wrap.
open_message enforces the binding triad: inner Schnorr signature valid, and
inner kind/channel/epoch equal to the outer kind and to the specific
channel/epoch whose key decrypted the payload (strict equality, never a
membership test). That defeats insider replay/splice across type, channel, or
epoch — the threat that any member, holding the channel key, could otherwise lift
another member’s signed content into a different context.
Structs§
- Opened
Message - A successfully opened and fully-verified Community message.
Enums§
- Envelope
Error - Errors from sealing or opening a Community message envelope.
Functions§
- build_
inner_ event - Build the inner authorship-proof event UNSIGNED, so the caller can sign it with
whatever the account uses — local keys OR a NIP-46 remote bunker (parity with the
DM send path, which signs through the active
NostrSigner).authoris the identity pubkey the signer will sign as. - build_
inner_ full - Like
build_inner_typedbut also carries a slice of EXTRA inner tags appended verbatim — used for NIP-92imetaattachment tags (a 3300 message mixing a caption with N files, viaattachments::attachment_to_imeta). They are added before signing, so the inner signature covers them; readers pick out what they need by exact tag name. - build_
inner_ typed - Like
build_inner_eventbut for any append-plane sub-type — a reaction (3301) or edit (3302) as well as a message (3300). Thereferenceetag points at the target: the replied-to message (3300), the reacted-to message (3301), or the edited message (3302). The inner kind is mirrored to the outer on seal, and the receiver enforces the binding triad (kind/channel/epoch). - open_
message - Open and fully verify an outer wire event, given the channel key + the exact channel/epoch coordinate that key belongs to.
- open_
message_ multi - Open an outer wire event when the member may hold MULTIPLE epoch keys (post-rekey catch-up): select
the decryption key by the outer’s
zpseudonym tag — each epoch addresses a distinct pseudonym we can recompute — then open under that exact epoch.epoch_keysis the member’s retained(epoch, key)set for this channel. Azmatching no held epoch yieldsEnvelopeError::NoHeldEpoch(not ours to read), keeping the per-event cost one pseudonym derivation per held epoch (a handful), no trial-decrypt. - seal_
message - Seal a plaintext message into an outer wire event. The outer event is signed by a fresh one-time key (no persistent author↔channel linkage on the wire).
- seal_
message_ with_ ephemeral - Like
seal_messagebut the caller supplies (and may retain) the ephemeral outer-signing key. Retaining it enables a later NIP-09 deletion of this exact outer event (the deletion must be signed by the same key — deletable messages), which is also how on-relay tests clean up after themselves. - seal_
with_ signed_ inner - Seal an already-signed inner authorship event into the outer wire event. The inner may have been signed by local keys or a remote bunker — this stage is signer-agnostic. Defensively re-checks the binding (kind/channel/epoch) so a caller can never seal an inner that the receiver would then reject as a splice.