Skip to content

Packet mumcord history

Tobias Vorwachs edited this page Oct 26, 2026 · 1 revision

Part of the Plugin data protocol. Flow: Flow-History-Sync (read it for chunking, reassembly and timeouts).

Packet mumcord.history

Purpose. Mumble servers keep no chat history. When a Mumcord user joins or follows a channel, or asks for earlier messages, their client asks other Mumcord clients for the channel messages from before it started receiving the channel live, and fetches only what it is missing.

dataID mumcord.history
Encoding compact JSON header, optionally \n and a binary body
Version sent / accepted 1 / 1 and higher (below 1 is dropped)
Sent to areq to everyone on the server; every other packet to one person
Feature gate none (only Mumcord clients answer)
Defined in crates/engine/src/history_sync/wire.rs, core.rs, hash.rs

Packet layout

{"v":1,"t":"part","r":7,"i":0,"last":false}\n<body bytes>

The header ends at the first \n; everything after it is the body. Packets without a body have no \n. A packet is at most 1000 bytes in total (PLUGIN_DATA_MAX), and the decoder rejects larger ones.

Header fields: v (version), t (packet type), r (request id chosen by the sender of the request, u32), plus the fields below. Times are Unix milliseconds. c is the Mumble channel id.

t Direction Header fields Body Meaning
areq requester to everyone r, c, from, to none Who holds messages of channel c in [from, to)?
avail holder to requester r, c, n, old, new none The holder has n messages, from old to new.
hreq requester to holder r, c, from, to none Send me the hashes of your messages in [from, to).
hashes holder to requester r, i, last 8-byte hashes, concatenated Chunk i of the hash list, oldest first.
get requester to holder r bitmap Send the messages whose bits are set (bit k % 8 of byte k / 8, least significant bit first, is hash k).
part holder to requester r, i, last slice of a JSON array Chunk i of the message stream; join all chunks to get the array.
deny holder to requester r none The holder does not answer request r.

Field types: r u32, c u32, n u32, i u16, last boolean, from, to, old, new i64. Packets that must have no body (areq, avail, hreq, deny) are rejected if they carry one; a hashes body whose length is not a multiple of 8 is rejected.

Message stream (part bodies)

The bodies of all part packets of one request, joined in order of i, form one JSON array of messages:

Field Type Meaning
ts integer Time the holder received it, Unix milliseconds.
n string Sender name (kept up to 128 characters).
h string Sender's certificate hash, hex, at most 128 characters. Optional.
b string Message body as sanitized HTML, at most 8 KiB. The receiver sanitizes it again.

Examples

{"v":1,"t":"areq","r":7,"c":3,"from":1766620800000,"to":1767225600000}
{"v":1,"t":"avail","r":7,"c":3,"n":120,"old":1766700000000,"new":1767200000000}
{"v":1,"t":"hreq","r":8,"c":3,"from":1766620800000,"to":1767225600000}
{"v":1,"t":"hashes","r":8,"i":0,"last":true}\n<n x 8 bytes>
{"v":1,"t":"get","r":8}\n<bitmap, 1 bit per hash>
{"v":1,"t":"part","r":8,"i":0,"last":true}\n[{"ts":1767200000000,"n":"Alice","h":"0123456789abcdef0123456789abcdef01234567","b":"Hello &lt;3"}]
{"v":1,"t":"deny","r":8}

Hashes

A hash is the first 8 bytes (HASH_LEN) of SHA-1 over these parts, each followed by a zero byte: mumcord-history-v1, the channel path (channel names joined with /), the sender (cert:<hash> or name:<name>), the send time in whole seconds as a decimal string, and the normalized body (tags removed, common HTML entities decoded, whitespace collapsed). Because every client stamps messages with its own clock, comparisons also try the second before and after; messages from the same sender with the same text within 5 seconds (SKEW_MS) also count as duplicates. See hash.rs.

Limits

Limit Value Constant
Plugin data per packet 1000 bytes PLUGIN_DATA_MAX
Body per packet 900 bytes BODY_BUDGET
Hashes per hashes chunk 112 HASHES_PER_CHUNK
Messages per request 500 MAX_MESSAGES
Body of one message 8 KiB (larger ones, such as inline images, are not synced) MAX_BODY
Message stream per answer 256 KiB MAX_STREAM
Age 7 days MAX_AGE_MS
Holders asked 3 MAX_HOLDERS
Answers prepared at once 3 MAX_SERVES
Requests per person refill 0.2 per second, burst 6 PEER_RATE, PEER_BURST
Outgoing pace 3 packets per second, burst 10 SEND_RATE, SEND_BURST

Privacy

Carries chat messages, sender names and certificate hashes of a channel. Only answered for people the holder saw arrive in that channel, and only for messages from before they arrived. Holders answer only while the user has "share history" on, and never for private chats. Received bodies are untrusted HTML and must be sanitized before rendering.

Used in

Flow-History-Sync.

Clone this wiki locally