Skip to main content

Module chat

Module chat 

Source
Expand description

CORD-03 Chat Plane — a Channel’s messages over the v2 stream envelope.

Every chat action is an unsigned rumor in an encrypted seal (CORD-02 §5 makes 20013 mandatory here — chat content must never be liftable as a standalone public event) inside a wrap at the Channel’s stream address. The rumor kinds reuse standard Nostr shapes wherever one fits:

  • kind 9 message (NIP-C7: content = text, replies via a q tag naming the parent RUMOR id — never the outer wrap’s, which differs per re-wrap)
  • kind 7 reaction (NIP-25: e/p/k name the target)
  • kind 5 delete (NIP-09: e = the author’s own rumor id, k its kind)
  • kind 3302 edit (e = own message rumor id, content = replacement text)
  • kind 3310 WebXDC peer signal (payload opaque to the protocol)
  • kind 23311 typing (ephemeral tier — rides the 21059 wrap)

Two Vector inner-tag conventions carry over from v1: NIP-30 ["emoji", shortcode, url] tags and verbatim extra tags (NIP-92 imeta attachments) ride inside the signed rumor, so they are author-committed.

Every rumor MUST commit ["channel", id] + ["epoch", n], checked strict-equal against the coordinate whose key decrypted the wrap (CORD-03 §3) — the rumor’s own claim is never trusted, so a keyholder of two planes cannot re-seal a rumor across Channels or replay it across epochs.

The wrap kind (1059/21059) is a transport tier, not a content authority: the open side admits any allowlisted rumor kind on either wrap and lets the rumor kind govern meaning. Publishers still MUST put typing on 21059 (relays MUST NOT store it) — that is a send-side duty, not a read gate.

Structs§

ReplyRef
A parsed reply reference — the NIP-C7 q tag’s parent rumor id and (when carried, a SHOULD upstream) its author.

Enums§

ChatError
Errors from the chat plane layer (envelope errors ride inside).
ChatEvent
A fully verified, typed chat event. Every variant keeps its OpenedStream — the proven author, rumor id, and ms time live there.

Functions§

build_comment_rumor
Build a kind-1111 threaded-reply rumor (NIP-22, CORD-03 §3). Uppercase K/E/P pin the immutable thread ROOT, lowercase k/e/p the immediate PARENT — all rumor ids. parent_root names the parent’s own root when the parent is itself a reply (inherited verbatim, so the root stays stable at any depth — the exact shape Armada builds); None means the parent IS the root.
build_delete_rumor
Build a kind-5 delete rumor (NIP-09): e = the author’s OWN rumor id, k = its kind. Semantic within the plane only — members stop rendering; the wrap ciphertext on relays needs a separate NIP-09 scrub by its p tag.
build_edit_rumor
Build a kind-3302 edit rumor: e = the author’s own message rumor id, content = the replacement text (fields unpinned upstream; this shape matches the CORD examples).
build_message_rumor
Build a kind-9 message rumor. reply_to is the parent’s (rumor_id_hex, author_hex) — the NIP-C7 q tag; emoji the NIP-30 (shortcode, url) pairs for any :shortcode: in the content; extra_tags ride verbatim (NIP-92 imeta attachments), author-committed by the seal.
build_reaction_rumor
Build a kind-7 reaction rumor (NIP-25): e = the target rumor id, p its author, k = the target’s kind (9 for a message, 1111 for a threaded reply). emoji_content is the reaction itself ("+", an emoji, or a :shortcode:); emoji carries the NIP-30 pair when the content is a custom-emoji shortcode.
build_typing_rumor
Build a kind-23311 typing rumor — presence of the event is the signal, it carries nothing. Seal it with ephemeral: true so relays never store it.
build_webxdc_rumor
Build a kind-3310 WebXDC peer-signal rumor: content and extra_tags are the app payload, opaque to the protocol, carried verbatim.
chat_group_key
A Channel’s Chat Plane group key. secret is whatever feeds the Channel at this epoch: the community_root for a Public Channel (at the root epoch), or the Channel’s independent key for a Private one (at its own channel epoch). The channel id inside the derivation gives every Channel a distinct address regardless of which secret feeds it.
open_chat_event
Open and fully verify one chat wrap against the (channel, epoch) whose key is being tried: envelope verification (stream::open_wrap), the encrypted-seal gate, the strict channel/epoch binding, the kind allowlist, then the typed parse. Malformed targets are errors, never panics.
open_chat_event_multi
Open a chat wrap against every (epoch, secret) a client holds for one Channel — the CORD-03 §3 read path, where history spanning a rekey is queried across all held epoch pubkeys. Selection is by the wrap’s author (each epoch’s derived address), NEVER trial decryption, and the binding is then enforced against the epoch that actually matched — so an epoch-N rumor re-sealed under epoch M’s key still dies as a splice.
seal_chat_rumor
Seal a chat rumor into its wrap: encrypted seal (mandatory on this plane), then the durable 1059 wrap — or the ephemeral 21059 when ephemeral (the typing tier). Refuses non-chat rumor kinds so a control edition can never be published onto a Channel’s stream by mistake. Returns the wrap plus the ephemeral p keypair (retain it to best-effort NIP-09-scrub the wrap later). Local-keys convenience; bunker accounts use stream::seal_content