Rollup release covering builds 126 → 129 since v1.0-build125.
Headline is a production crash fix in the SRTP transport that
surfaced overnight after 16h+ of soak on build 125. Plus two
Settings UI changes and ConnectionLink schema cleanup.
Fixed a concurrent-write race in srtpwrap.wrappedConn.Write that
caused a panic: d.nx != 0 in crypto/sha1.(*digest).checkSum
during production soak of build 125 (2026-05-21 22:22:38). Two
goroutines from runSRTPSession — the probe sender and the send
goroutine — both called srtpConn.Write on the same wrappedConn
instance, and wrappedConn.Write only held its mutex for the
seq/ts increment, releasing it before pion's
srtp.Context.EncryptRTP (which is not safe for concurrent use —
its HMAC-SHA1 keeps internal state across Sum() calls).
Trigger: phone wakes after iOS freeze → all 30 conns fire
active-probe-on-wake at the same instant WG data resumes through
sendCh. The race window is small in steady state but opens on
every wake event; the longer the soak, the more likely a wake batch
coincides with active WG traffic.
This is a regression introduced in build 125 — builds 122-124 had
only one writer (the send goroutine); build 125's probe-sender
addition opened the second writer without updating the locking
discipline. The build-123 "no mutex needed because dedicated
read/write goroutines" assumption was correct at the time, broken
by build 125.
Fix: extend c.mu over the entire Write method (defer c.mu.Unlock() at top). Contention is minimal — per-instance
contention only fires when probe sender and send goroutine try to
Write within the same window, which is rare in steady state and
only frequent under wake-event storms (where it serializes safely
without throughput impact). Wire format and crypto unchanged.
Same fix applied defensively to the server side in
anton48/vk-turn-proxy add-server-srtp-layer (commit c737859),
though the server's current pumpBidirectional has only one writer
per conn and isn't actively triggered today. Back-compat with any
client/server build combination.
Build 127 — removed "DTLS Obfuscation" toggle. The toggle was
misleading: on the SRTP path it was ignored entirely (dispatcher
prefers UseSrtp in runConnection), and on the legacy path
turning it OFF landed in runDirectSession where 30 conns
"established" but no real traffic flowed (verified 2026-05-22:
30 conns allocated, conn-stats final showed 30 idle with 0 RX —
tunnel up by NEVPNStatus but no actual internet). The
@AppStorage("useDTLS") declaration stays (default true), still
round-trips through BackupManager — power users can still flip
it via Full Backup export/edit/import or Connection Link for
debugging.
Build 128 — added "Use UDP transport to TURN" toggle. Surfaces
the existing useUDP setting that was already wired through Go-
side since build 109 but had no UI affordance. Default off
(TCP-control, bypasses VK's per-cred allocation-rate throttle
introduced 2026-05-18 — empirically ~0% quota errors on TCP vs
36-58% on UDP for the same cred). Toggle on only if your network
blocks or throttles TCP-to-relay and you'd rather take the
allocation-rate hit than fail to connect. Independent of the
SRTP / DTLS+WG transport choice — controls the iOS ↔ TURN-relay
control channel leg.
Empirically tested all 4 combinations (SRTP/TCP, SRTP/UDP,
DTLS+WG/TCP, DTLS+WG/UDP) on iPhone build 128 — all 4 work.
ConnectionSettings.useDTLS / useWrap / wrapKeyHex made Optional
in Codable, matching the pattern already in place for useSrtp /
useUDP. Admins generating connection links with quick_link.py
no longer need to carry the legacy WRAP fields if their deployment
doesn't use them. Back-compat preserved: older links that still
include these fields apply them through unchanged.
quick_link.py at the repo root updated correspondingly:
useSrtpanduseUDPadded as optional fields with explanatory
docstring entriesuseDTLS/useWrap/wrapKeyHexmoved out of REQUIRED into
the optional section (their UI toggles are long-gone — useDTLS
removed build 127, useWrap removed build 115)- Minimum required fields: WG identity (privateKey / peerPublicKey
/ presharedKey) + tunnel/peer addresses +vkLink. 7 fields
total; everything else is overridable defaults
Compat caveat: links generated by the new quick_link.py that
omit useDTLS/useWrap/wrapKeyHex won't decode on iOS builds 128 or
earlier (their Codable still requires those three fields). For
mixed-importer deployments, keep the fields in CONFIG. For pure
129+ deployments, delete them for a leaner payload.