feat(host/wire): MTU resilience for the video data plane #37

Merged
enricobuehler merged 1 commits from worktree-wire-mtu-resilience into main 2026-08-04 17:02:04 +00:00
Owner

Problem

Video datagrams are sealed at a shard payload sized for a clean 1500-byte MTU (1472-byte UDP payloads). A host whose route to the client crosses a smaller-MTU hop — typically a VPN/overlay adapter (Tailscale / Cloudflare WARP / ZeroTier default to 1280) claiming the LAN route, or a lowered NIC MTU — delivers every small flow (QUIC control, hole punch, input, audio) while 100 % of video datagrams die: the client sits on a black screen reporting loss_ppm=0 (it cannot see gaps in packets it never saw any of), and the host streams into the void with every gauge green. Field-reported 2026-08-04 as "connects fine, black screen forever" on both Android and Linux clients, after days of codec/HDR guessing.

Change — three legs, none of which alters a session on a healthy path

  • PUNKTFUNK_WIRE_MTU operator override — shard payload derived from a given on-wire IP MTU. Wire-compatible with every deployed client: Welcome::shard_payload is already negotiated per session (the v4/v6 split ships two values today) and clients follow the negotiated value.
  • Detection — the QUIC MTU-discovery probe ceiling moves from quinn's stock 1452 to exactly the sealed video-datagram size (1472), so a control connection's settled MTU becomes a verdict on the path: settled at the ceiling proves it carries video, settled below proves it cannot. A per-session watcher samples after the search settles (with a live-connection guard against mid-search false learns on high-RTT paths) and logs an actionable WARN naming the failure shape, the ping -f discriminator pair, and netsh.
  • Healing — the measured budget is recorded per peer IP; the next handshake clamps shard_payload to fit, so a reconnect self-heals. A later session that reaches the ceiling erases the record (self-correcting in both directions).

Regression surface for unaffected users

The only universal delta is QUIC MTUD probing 20 bytes larger (1452 → 1472); probes are padded PINGs quinn already expects to lose, and on clean paths QUIC gains the headroom. Everything else is dormant until a path is proven constrained. Residual: a severely lossy connect window could settle below-ceiling and clamp the next session's shards a few percent smaller — mildly more packets, still correct, self-erasing on any clean re-measure.

Verification

  • core: 286/286 --lib --features quic + clippy -D warnings (macOS)
  • host: clippy -D warnings + native:: tests 44/44 incl. 5 new resolution tests (pf-lxcheck Linux container)
  • fmt clean; regenerated C header committed (new MIN_SHARD_PAYLOAD define)
  • owed: real-network A/B on the field reporter's VPN-afflicted host — expect session 1 black + the new WARN naming the adapter, session 2 self-healed to a working picture

Follow-on (planned, not in this PR)

Mid-session healing + opt-in jumbo (9000-MTU) via a capability-gated ShardPayloadChanged/Ack pair with per-frame self-describing wire geometry — plan in punktfunk-planning design/shard-payload-reneg.md (0674ed2).

## Problem Video datagrams are sealed at a shard payload sized for a clean 1500-byte MTU (1472-byte UDP payloads). A host whose route to the client crosses a smaller-MTU hop — typically a VPN/overlay adapter (Tailscale / Cloudflare WARP / ZeroTier default to 1280) claiming the LAN route, or a lowered NIC MTU — delivers every **small** flow (QUIC control, hole punch, input, audio) while **100 % of video datagrams die**: the client sits on a black screen reporting `loss_ppm=0` (it cannot see gaps in packets it never saw any of), and the host streams into the void with every gauge green. Field-reported 2026-08-04 as "connects fine, black screen forever" on both Android and Linux clients, after days of codec/HDR guessing. ## Change — three legs, none of which alters a session on a healthy path - **`PUNKTFUNK_WIRE_MTU` operator override** — shard payload derived from a given on-wire IP MTU. Wire-compatible with every deployed client: `Welcome::shard_payload` is already negotiated per session (the v4/v6 split ships two values today) and clients follow the negotiated value. - **Detection** — the QUIC MTU-discovery probe ceiling moves from quinn's stock 1452 to exactly the sealed video-datagram size (**1472**), so a control connection's settled MTU becomes a verdict on the path: settled *at* the ceiling proves it carries video, settled *below* proves it cannot. A per-session watcher samples after the search settles (with a live-connection guard against mid-search false learns on high-RTT paths) and logs an actionable WARN naming the failure shape, the `ping -f` discriminator pair, and `netsh`. - **Healing** — the measured budget is recorded per peer IP; the next handshake clamps `shard_payload` to fit, so **a reconnect self-heals**. A later session that reaches the ceiling erases the record (self-correcting in both directions). ## Regression surface for unaffected users The only universal delta is QUIC MTUD probing 20 bytes larger (1452 → 1472); probes are padded PINGs quinn already expects to lose, and on clean paths QUIC gains the headroom. Everything else is dormant until a path is *proven* constrained. Residual: a severely lossy connect window could settle below-ceiling and clamp the next session's shards a few percent smaller — mildly more packets, still correct, self-erasing on any clean re-measure. ## Verification - core: 286/286 `--lib --features quic` + clippy `-D warnings` (macOS) - host: clippy `-D warnings` + `native::` tests 44/44 incl. 5 new resolution tests (pf-lxcheck Linux container) - fmt clean; regenerated C header committed (new `MIN_SHARD_PAYLOAD` define) - ⏳ **owed: real-network A/B** on the field reporter's VPN-afflicted host — expect session 1 black + the new WARN naming the adapter, session 2 self-healed to a working picture ## Follow-on (planned, not in this PR) Mid-session healing + opt-in jumbo (9000-MTU) via a capability-gated `ShardPayloadChanged`/`Ack` pair with per-frame self-describing wire geometry — plan in `punktfunk-planning` `design/shard-payload-reneg.md` (`0674ed2`).
enricobuehler added 1 commit 2026-08-04 17:01:13 +00:00
feat(host/wire): MTU resilience for the video data plane
ci / docs-site (pull_request) Successful in 1m5s
ci / web (pull_request) Successful in 1m47s
apple / swift (pull_request) Successful in 1m22s
apple / screenshots (pull_request) Skipped
ci / rust-arm64 (pull_request) Successful in 2m46s
android / android (pull_request) Successful in 3m25s
windows / build (x86_64-pc-windows-msvc) (pull_request) Successful in 2m13s
windows / build (aarch64-pc-windows-msvc) (pull_request) Successful in 3m16s
ci / rust (pull_request) Successful in 22m23s
4bc7eecf05
Video datagrams are sealed at a shard payload sized for a clean 1500-byte
MTU (1472-byte UDP payloads). A host whose route to the client crosses a
smaller-MTU hop (a VPN/overlay adapter claiming the LAN route, a lowered
NIC MTU) delivers every small flow — QUIC control, hole punch, input,
audio — while 100% of video datagrams die: the client sits on a black
screen reporting zero loss and the host streams into the void with every
gauge green. Field-reported as 'connects fine, black screen forever'.

Three legs, none of which changes a session on a healthy path:

- PUNKTFUNK_WIRE_MTU operator override: shard payload derived from a
  given on-wire IP MTU. Wire-compatible — Welcome::shard_payload is
  already negotiated per session (the v4/v6 split ships two values
  today) and every client follows the negotiated value.
- Detection: the QUIC MTU-discovery probe ceiling moves from quinn's
  stock 1452 to exactly the sealed video-datagram size (1472), so a
  control connection's settled MTU becomes a verdict on the path:
  settled at the ceiling proves it carries video, settled below proves
  it cannot. A per-session watcher samples after the search has settled
  (live-connection guard against mid-search false learns) and logs an
  actionable WARN naming the failure shape and the diagnosis commands.
- Healing: the measured budget is recorded per peer IP; the next
  handshake clamps shard_payload to fit, so a reconnect self-heals. A
  later session that reaches the ceiling erases the record.

Verified: core 286/286 --features quic + clippy -D warnings (macOS);
host clippy -D warnings + native:: tests 44/44 (pf-lxcheck container).
The regenerated C header picks up the new MIN_SHARD_PAYLOAD constant.
enricobuehler merged commit 69f1db5ea9 into main 2026-08-04 17:02:04 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: unom/punktfunk#37