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
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.
SessionKeyRequest
A request from a recently-joined member: “I need session keys”. Existing members respond with MemberAnnounce.
Encrypted
An encrypted message in an encrypted room.
Fields
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).
Plain
A plaintext message in an unencrypted room.
Fields
MemberLeave
Explicit leave notification.
Fields
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
Typing
Ephemeral “I’m typing” signal. TTL on the receive side is 3s.
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
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
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.
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.
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.
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.
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.
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.
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).
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
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].
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
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
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
new_ciphertext_b64: Stringbase64 MegolmMessage bytes of the replacement body, for encrypted
rooms. Empty for plaintext rooms (which carry the new body in
new_body instead).
session_id: Stringhuddle 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.
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.
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
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.
MlsWelcome
A Welcome hands a freshly-added member the group’s secrets. MUST be
WireMessage::Signed; directed at one new member.
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
MlsApplication
An MLS-encrypted application (chat) message under the current epoch key.
Trait Implementations§
Source§impl Clone for RoomMessage
impl Clone for RoomMessage
Source§fn clone(&self) -> RoomMessage
fn clone(&self) -> RoomMessage
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more