Skip to main content

Module envelope

Module envelope 

Source
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§

OpenedMessage
A successfully opened and fully-verified Community message.

Enums§

EnvelopeError
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). author is the identity pubkey the signer will sign as.
build_inner_full
Like build_inner_typed but also carries a slice of EXTRA inner tags appended verbatim — used for NIP-92 imeta attachment tags (a 3300 message mixing a caption with N files, via attachments::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_event but for any append-plane sub-type — a reaction (3301) or edit (3302) as well as a message (3300). The reference e tag 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 z pseudonym tag — each epoch addresses a distinct pseudonym we can recompute — then open under that exact epoch. epoch_keys is the member’s retained (epoch, key) set for this channel. A z matching no held epoch yields EnvelopeError::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_message but 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.