Skip to main content

RoomMessage

Enum RoomMessage 

Source
pub enum RoomMessage {
Show 27 variants MemberAnnounce { sender_fingerprint: String, wrapped_session_key: Option<String>, display_name: Option<String>, sender_ed25519_pubkey: Option<String>, sender_mlkem_pubkey: Option<String>, mlkem_ciphertext: Option<String>, }, SessionKeyRequest { requester_fingerprint: String, }, Encrypted { sender_fingerprint: String, session_id: String, ciphertext_b64: String, client_msg_id: Option<String>, reply_to: Option<String>, }, Plain { sender_fingerprint: String, body: String, client_msg_id: Option<String>, reply_to: Option<String>, }, MemberLeave { sender_fingerprint: String, room_id: Option<String>, }, RotateRoomKey { rotator_fingerprint: String, new_salt: Vec<u8>, room_id: Option<String>, }, Typing { sender_fingerprint: String, }, FileOffer { sender_fingerprint: String, file_id: String, name: String, size_bytes: u64, mime: Option<String>, chunk_count: u32, encrypted_meta: Option<EncryptedFileMeta>, }, FileChunk { sender_fingerprint: String, file_id: String, chunk_index: u32, total_chunks: u32, data_b64: String, }, OwnerGrant { room_id: String, target_fingerprint: String, }, BanMember { room_id: String, target_fingerprint: String, }, SasInit { tx_id: String, ephemeral_x25519_pubkey_b64: String, target_fingerprint: String, }, SasResponse { tx_id: String, ephemeral_x25519_pubkey_b64: String, }, SasConfirm { tx_id: String, matched: bool, }, JoinRefused { room_id: String, target_fingerprint: String, reason: String, }, CodeJoinRequest { room_id: String, joiner_x25519_pubkey_b64: String, code: String, }, CodeJoinResponse { room_id: String, target_fingerprint: String, owner_x25519_pubkey_b64: String, owner_session_id: String, wrapped_session_key_b64: String, nonce_b64: String, }, ProfileUpdate { sender_fingerprint: String, username: Option<String>, updated_at: i64, }, ContactRequest { requester_fingerprint: String, display_name: Option<String>, note: Option<String>, sender_ed25519_pubkey: Option<String>, }, Reaction { sender_fingerprint: String, target_msg_id: String, emoji: String, removed: bool, }, Edit { sender_fingerprint: String, target_msg_id: String, new_ciphertext_b64: String, session_id: String, new_body: Option<String>, }, Delete { sender_fingerprint: String, target_msg_id: String, }, RoomSetting { sender_fingerprint: String, disappearing_ttl_secs: u64, room_id: Option<String>, }, MlsKeyPackage { sender_fingerprint: String, key_package_b64: String, }, MlsWelcome { target_fingerprint: String, welcome_b64: String, }, MlsCommit { sender_fingerprint: String, commit_b64: String, }, MlsApplication { sender_fingerprint: String, ciphertext_b64: String, },
}
Expand description

All messages on a room’s per-room topic.

Variants§

§

MemberAnnounce

Announce my presence in the room. For encrypted rooms, also share my Megolm session key (passphrase-wrapped).

Fields

§sender_fingerprint: String
§wrapped_session_key: Option<String>

base64(nonce || chacha20poly1305_ciphertext) of the Megolm SessionKey, encrypted under the passphrase-derived key. None for unencrypted rooms.

§display_name: Option<String>

Optional human-readable display name. Serde defaults to None for forward compat with older peers.

§sender_ed25519_pubkey: Option<String>

Base64 of the sender’s 32-byte Ed25519 public key. Lets every existing member learn the new member’s pubkey on first contact, so they can verify future SignedRoomMessage envelopes from this fingerprint. #[serde(default)] for forward compat: a pre-0.3.0 peer that doesn’t send this still works, but its signed messages can’t be verified until it re-announces.

§sender_mlkem_pubkey: Option<String>

huddle 1.3: base64 of the sender’s 1184-byte ML-KEM-768 encapsulation (public) key, for hybrid post-quantum DM key agreement. Populated only on Direct room announces. Its presence is how the peer signals PQ capability: a DM goes hybrid iff the partner published this (or we have it pinned from a prior announce — see app::ensure_dm_key). #[serde(default, skip_serializing_if = "Option::is_none")] keeps pre-1.3 peers (and all group announces) byte-compatible — when None the field simply doesn’t appear, and a missing field decodes back to None. (Do NOT “simplify” this to serde’s bare skip: that would drop the field on the wire unconditionally and silently disable the whole PQ path.) Carried inside the signed MemberAnnounce envelope, so the relay can’t strip it to force a downgrade without breaking the signature.

§mlkem_ciphertext: Option<String>

huddle 1.3: base64 of the 1088-byte ML-KEM-768 ciphertext sent by the DM initiator (the lower-fingerprint peer) to the responder, who decapsulates it to recover the shared post-quantum secret. Only the initiator sets this; the responder’s announces omit it.

§

SessionKeyRequest

A request from a recently-joined member: “I need session keys”. Existing members respond with MemberAnnounce.

Fields

§requester_fingerprint: String
§

Encrypted

An encrypted message in an encrypted room.

Fields

§sender_fingerprint: String
§session_id: String
§ciphertext_b64: String

base64-encoded MegolmMessage bytes

§client_msg_id: Option<String>

huddle 2.0 (F10): sender-minted stable id for this message, used as the cross-peer targeting key for reactions, edits, replies and deletes. #[serde(default, skip_serializing_if = "Option::is_none")] keeps pre-2.0 peers byte-compatible: when None the field is omitted on the wire, and a missing field decodes back to None (so messages from older peers simply can’t be a content-affordance target — content still flows).

§reply_to: Option<String>

huddle 2.0 (F10): client_msg_id of the message this one replies to, if any. None (and omitted on the wire) for top-level messages and pre-2.0 senders.

§

Plain

A plaintext message in an unencrypted room.

Fields

§sender_fingerprint: String
§body: String
§client_msg_id: Option<String>

huddle 2.0 (F10): see Encrypted::client_msg_id. Sender-minted stable id; #[serde(default, skip_serializing_if = "Option::is_none")] for byte-compat with pre-2.0 peers.

§reply_to: Option<String>

huddle 2.0 (F10): client_msg_id of the message this one replies to, if any. None for top-level messages and pre-2.0 senders.

§

MemberLeave

Explicit leave notification.

Fields

§sender_fingerprint: String
§room_id: Option<String>

huddle 2.0.3 (audit N-M2): the room this leave is for. The Ed25519 signature commits to the payload but NOT the gossip topic, so without this a malicious relay could replay a signed leave from room A onto room B’s topic. Option + skip_serializing_if keeps the wire byte-identical to pre-2.0.3 when unset (graceful back-compat); honest receivers cross-check it against the delivery topic when present.

§

RotateRoomKey

“I’m rotating the room key — derive a new passphrase key from new_salt + the new passphrase you’ll be told out-of-band, then wait for my MemberAnnounce.” Phase 3 v1: simplistic — only the rotator’s outbound changes; receivers must opt in by entering the new passphrase to decrypt new wrapped session keys.

Fields

§rotator_fingerprint: String
§new_salt: Vec<u8>

Argon2id salt for the new passphrase-derived key.

§room_id: Option<String>

huddle 2.0.3 (audit N-M2): the room being rotated, cross-checked against the delivery topic so a signed rotation can’t be replayed into another room. Optional for pre-2.0.3 back-compat.

§

Typing

Ephemeral “I’m typing” signal. TTL on the receive side is 3s.

Fields

§sender_fingerprint: String
§

FileOffer

Announce a file the sender is about to push. The receiver creates an attachment row (status=offered) and waits for chunks. For encrypted rooms encrypted_meta carries the Megolm-wrapped file key + ChaCha20 nonce.

Fields

§sender_fingerprint: String
§file_id: String
§name: String
§size_bytes: u64
§chunk_count: u32
§encrypted_meta: Option<EncryptedFileMeta>
§

FileChunk

One chunk of an in-flight file. Receivers reassemble by index and verify the final SHA-256 against file_id.

Fields

§sender_fingerprint: String
§file_id: String
§chunk_index: u32
§total_chunks: u32
§data_b64: String

base64 of raw chunk bytes (plaintext bytes for unencrypted rooms, ChaCha20-Poly1305 ciphertext for encrypted).

§

OwnerGrant

Phase B: an existing owner promotes target_fingerprint to owner. MUST be sent inside WireMessage::Signed — the signer must be on the room’s current owner_fingerprints list for honest receivers to apply the change.

Fields

§room_id: String
§target_fingerprint: String
§

BanMember

Phase B: an existing owner bans target_fingerprint from the room. MUST be sent inside WireMessage::Signed. Honest clients then ignore the banned fingerprint’s MemberAnnounce + messages. The cryptographic enforcement is the immediate RotateRoomKey that the banning owner sends right after.

Fields

§room_id: String
§target_fingerprint: String
§

SasInit

Phase G: SAS verification step 1. The initiator picks a random tx_id and an ephemeral X25519 keypair, sends the pubkey. MUST be sent inside WireMessage::Signed so the receiver can bind this ephemeral key to the initiator’s Ed25519 identity.

Fields

§tx_id: String
§ephemeral_x25519_pubkey_b64: String
§target_fingerprint: String
§

SasResponse

Phase G: SAS step 2 — responder’s ephemeral X25519 pubkey. Both sides now have what they need to compute the shared secret and derive the SAS code locally. Signed.

Fields

§tx_id: String
§ephemeral_x25519_pubkey_b64: String
§

SasConfirm

Phase G: SAS step 3 — once both sides have OOB-compared the derived code and pressed “Match”, each broadcasts this. On receiving the partner’s matched=true, the local side flips verified=1 for the partner’s fingerprint. Signed.

Fields

§tx_id: String
§matched: bool
§

JoinRefused

Phase E: an existing owner of a verified_only room is telling target_fingerprint (an unverified joiner) why their announce went unanswered. Replaces a silent hang on the joiner’s side. Signed.

Fields

§room_id: String
§target_fingerprint: String
§reason: String
§

CodeJoinRequest

Phase F: a joiner is asking to enter a room using a short-lived owner-issued code (no passphrase). Includes the joiner’s ephemeral X25519 pubkey for ECDH key delivery. Signed (so the owner knows who’s asking).

Fields

§room_id: String
§joiner_x25519_pubkey_b64: String
§code: String
§

CodeJoinResponse

Phase F: an issuing owner’s response to a valid CodeJoinRequest. Carries the owner’s ephemeral X25519 pubkey + the current Megolm session key wrapped under the ECDH-derived key. Joiner does X25519 the other direction, derives the same wrap key, unwraps the session key. Signed.

Fields

§room_id: String
§target_fingerprint: String
§owner_x25519_pubkey_b64: String
§owner_session_id: String
§wrapped_session_key_b64: String
§nonce_b64: String
§

ProfileUpdate

Phase 0.5: a peer is announcing (or clearing) their self-declared username. MUST be sent inside WireMessage::Signed — receivers require verified_signer == sender_fingerprint. Last-write-wins by updated_at (monotonic ms). username = None clears the previously-set username and the peer renders as [anonymous].

Fields

§sender_fingerprint: String
§username: Option<String>
§updated_at: i64
§

ContactRequest

huddle 1.0: a contact/DM request delivered to the target’s relay inbox (inbox_room_id). MUST be sent inside WireMessage::Signed — the signer’s fingerprint IS the requester’s identity (the whole point: it proves who’s asking). Carries the requester’s Ed25519 pubkey so the recipient can TOFU-pin it and later derive the DM ECDH key without a round-trip.

Fields

§requester_fingerprint: String
§display_name: Option<String>
§sender_ed25519_pubkey: Option<String>
§

Reaction

huddle 2.0 (F10): add or remove an emoji reaction on another message in this room. MUST be sent inside WireMessage::Signed — the signer must equal sender_fingerprint; honest receivers drop unsigned reactions (like FileOffer / MemberAnnounce). The toggle unit is one emoji per (sender, target message): removed = false adds the reaction, removed = true clears it. A brand-new variant, so pre-2.0 peers never produce it and ignore it on receipt.

Fields

§sender_fingerprint: String
§target_msg_id: String

client_msg_id of the message being reacted to (must exist in this room for honest receivers to apply the reaction).

§emoji: String

A single emoji or short custom code (e.g. “+1”, “laugh”).

§removed: bool

false = add the reaction, true = remove it (toggle off). #[serde(default)] so a missing field decodes to false (add).

§

Edit

huddle 2.0 (F10): edit the body of an existing message. MUST be sent inside WireMessage::Signed; honest receivers apply it only when the signer is the original sender OR a current room owner (moderation). Last-write-wins. For encrypted rooms the replacement body rides as a fresh Megolm ciphertext in new_ciphertext_b64 (decrypted exactly like an Encrypted body, against the carried session_id); for plaintext rooms it rides as new_body.

Fields

§sender_fingerprint: String
§target_msg_id: String

client_msg_id of the message being edited.

§new_ciphertext_b64: String

base64 MegolmMessage bytes of the replacement body, for encrypted rooms. Empty for plaintext rooms (which carry the new body in new_body instead).

§session_id: String

huddle 2.0 (F10): the Megolm session_id the editor encrypted new_ciphertext_b64 under, so receivers decrypt the edit against the correct session — exactly like Encrypted::session_id — with no reliance on an in-memory “last inbound session” guess (which failed after a session rotation, after restart, from a second device, or before any other message on the session). Empty (and omitted on the wire) for plaintext rooms. #[serde(default, skip_serializing_if = "String::is_empty")] keeps it additive: a pre-session-id edit decodes to an empty string and is dropped gracefully rather than mis-decrypted.

§new_body: Option<String>

Replacement plaintext body, for unencrypted rooms. None (and omitted on the wire) for encrypted rooms, whose new body lives in new_ciphertext_b64.

§

Delete

huddle 2.0 (F10): delete (tombstone) an existing message. MUST be sent inside WireMessage::Signed; honest receivers apply it only when the signer is the original sender OR a current room owner. Idempotent — the original message row is kept and a tombstone is recorded, so the body renders as “[deleted]” everywhere.

Fields

§sender_fingerprint: String
§target_msg_id: String

client_msg_id of the message being deleted.

§

RoomSetting

huddle 2.0 (F9): a signed control message that sets this room’s disappearing-messages TTL. MUST be sent inside WireMessage::Signed — honest receivers apply it only when the signer is the room creator or a current owner. disappearing_ttl_secs = 0 turns expiry off; any value

0 makes each peer locally auto-delete messages that many seconds after they were stored. Pre-2.0 peers ignore the unknown variant and never expire their local copies (graceful downgrade).

Fields

§sender_fingerprint: String
§disappearing_ttl_secs: u64
§room_id: Option<String>

huddle 2.0.3 (audit N-M2): the room this setting applies to. Without it a malicious relay could replay a signed disappearing-TTL from one room (where the signer is an owner) onto another room they also own, forcing a retroactive history purge there (chains L-24). Cross-checked against the delivery topic on receive. Optional for back-compat.

§

MlsKeyPackage

A member publishes its MLS KeyPackage so existing members can add it to the group.

Fields

§sender_fingerprint: String
§key_package_b64: String

base64 TLS-serialized MLS KeyPackage.

§

MlsWelcome

A Welcome hands a freshly-added member the group’s secrets. MUST be WireMessage::Signed; directed at one new member.

Fields

§target_fingerprint: String
§welcome_b64: String

base64 TLS-serialized MLS Welcome.

§

MlsCommit

A Commit advances the group’s epoch (add / remove / update). MUST be WireMessage::Signed; applied in the relay’s per-room seq order so every member converges on the same epoch sequence — TreeKEM forward secrecy, post-compromise security, and cryptographically-enforced removal.

Fields

§sender_fingerprint: String
§commit_b64: String

base64 TLS-serialized MLS handshake MlsMessage (a Commit).

§

MlsApplication

An MLS-encrypted application (chat) message under the current epoch key.

Fields

§sender_fingerprint: String
§ciphertext_b64: String

base64 TLS-serialized MLS application MlsMessage.

Trait Implementations§

Source§

impl Clone for RoomMessage

Source§

fn clone(&self) -> RoomMessage

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for RoomMessage

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for RoomMessage

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Serialize for RoomMessage

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V