Repository navigation
Packet mumcord history
Part of the Plugin data protocol. Flow: Flow-History-Sync (read it for chunking, reassembly and timeouts).
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
|
{"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.
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. |
{"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 <3"}]
{"v":1,"t":"deny","r":8}
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.
| 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
|
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.
Mumcord is licensed under the AGPL-3.0 (versions up to 0.1.1: MIT) · Source code · Website · Website source