Repository navigation
Flow Direct Connection
Tobias Vorwachs edited this page Oct 26, 2026
·
1 revision
Part of the Plugin data protocol. Packet: Packet-mumcord-p2p.
Two Mumcord clients open a direct, encrypted QUIC connection for large data. Plugin data carries only the introduction; the data itself never touches the Mumble server. Users: Flow-Full-Image and Flow-Screen-Share.
sequenceDiagram
participant A as Client A
participant S as Mumble server
participant B as Client B
A->>S: plugin data mumcord.p2p offer (s, candidates c)
S-->>B: relayed
Note over B: User must allow direct connections, otherwise decline
B->>S: plugin data mumcord.p2p answer (s, candidates c)
S-->>A: relayed
A-->>B: QUIC connect (direct), mutual TLS
Note over A,B: Each side accepts the other only if the certificate hash equals UserState.hash from the server
alt direct path works
A-->>B: feature streams (direct, server not involved)
else decline, no answer within 15 s or no path
Note over A,B: Feature falls back to plugin data, or is not available
end
- A feature (an image download or a screen share) wants a direct connection to a person who announced
p2p. - If the user has not decided yet, the client asks for consent and waits up to 60 seconds (
CONSENT_WAIT). Without an answer the feature falls back to plugin data and the question stays open. - With consent, A gathers address candidates: IPv6 and LAN addresses, and a router port mapping (PCP, NAT-PMP or UPnP, within about 3 seconds). It sends an
offerwith a fresh 16-hex-digit attempt idsand up to 8 candidates. - B, if it allows direct connections, gathers its own candidates and sends an
answerwith the sames. If not, it sendsdeclinewithr=declined; if it allowed but could not set up,unavailable. An offer that arrives before B's user decided is declined. - A waits up to 15 seconds (
ANSWER_WAIT) for answer or decline, then both sides try the candidates over QUIC (ALPNmumcord-p2p/1, connect timeout 10 seconds in the library). - Mutual TLS: each side presents its Mumble client certificate and accepts the peer only if the certificate hash equals the hash the Mumble server reported for that user (
UserState.hash). A mismatch ends the connection. - One connection per pair of users on a server is shared by all features. Each feature opens its own stream; the first byte is the stream kind.
The connection has a 30 second idle timeout with keep-alive pings every 10 seconds.
| Kind | Feature | Format |
|---|---|---|
1 |
Full image | Request: the image's SHA-256 (32 bytes). Reply: 0 and the size (u32, big-endian) followed by the image, or 1 (unavailable). Same checks as Packet-mumcord-image. A read may stall at most 15 seconds. |
2 |
Screen share | The sharer writes frames back to back. The viewer's direction carries single-byte requests; 1 asks for a keyframe, unknown bytes are ignored. |
Header of 20 bytes, big-endian, followed by the payload:
| Bytes | Field |
|---|---|
| 1 | Version, 1. Another version ends the stream. |
| 1 | Kind: 1 video (VP9), 2 audio (Opus) |
| 1 | Flags: bit 0 keyframe, all other bits zero |
| 1 | Temporal layer, at most 3 |
| 8 | Capture timestamp in microseconds |
| 2 | Width in pixels (0 for audio) |
| 2 | Height in pixels (0 for audio) |
| 4 | Payload size in bytes (not 0; video at most 4 MiB, audio at most 4 KiB) |
Video is at most 7680 x 4320. See crates/engine/src/screen_share/wire.rs.
| Situation | Result |
|---|---|
| User declines or has not allowed direct connections |
decline; feature falls back or is unavailable. |
| No answer within 15 s | The attempt ends; fallback. |
| No candidate reachable | Fallback; unavailable where the other side reports it. |
Certificate hash differs from UserState.hash
|
Connection refused. |
| More than 8 candidates, unusable addresses (port 0, unspecified, multicast, broadcast, IPv6 link-local, IPv4-mapped IPv6) | Rejected. |
| Unknown stream kind | Stream closed. |
Mumcord is licensed under the AGPL-3.0 (versions up to 0.1.1: MIT) · Source code · Website · Website source