Skip to content

Sync develop: call robustness, native codec, mute, doctor, client UI - #44

Merged
JotaDev66 merged 57 commits into
JotaDev66:developfrom
jobasfernandes:develop
Jul 27, 2026
Merged

Sync develop: call robustness, native codec, mute, doctor, client UI#44
JotaDev66 merged 57 commits into
JotaDev66:developfrom
jobasfernandes:develop

Conversation

@jobasfernandes

@jobasfernandes jobasfernandes commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Media & call robustness

  • Remove the maxOpenRelays cap that raced the is_fna relay shut, keeping maxDialRelays=3 (the peer's inbound media only arrives on the is_fna relay).
  • Reorder inbound RTP through a jitter buffer with loss concealment.
  • Demux RTP/RTCP by version and packet-type range.
  • Dial the FNA relay first on inbound calls.
  • Drop stale call offers replayed after an offline reconnect, plus a generation-token backstop for the replay window.
  • Let a same-device accept retry through the rekey path; stop sibling devices ringing when a companion answers.
  • Hold a call through a browser refresh (grace window) and auto-resume its audio.
  • Run media cleanup when StartCall fails before setup.

Native codec (CPU)

  • Native mlow encoder behind the nativemlow build tag.
  • Native and pure image variants in docker/CI, wiring the native encoder.

Mute

  • Sync mute state over mute_v2 signaling; mute button and peer-mute indicator in the client.

Doctor / STUN

  • WACALLS_STUN_SERVER config with public STUN defaults.
  • Probe STUN servers for the external public IP, evaluate it against config, and report the check in the preflight.

Session / API / onboarding UI

  • Rename-session endpoint; name sessions at create and rename via a shared dialog.
  • Account identity strip, guided pairing panel, first-run hero, session profile photo.
  • Relay pill on the identity strip; surface the connected relay over SSE.

Diagnostics & docs

  • Record a per-call event timeline to jsonl when WACALLS_DIAG_DIR is set.
  • Document WACALLS_STUN_SERVER and the doctor STUN check; list the call mute endpoint in the API table.

Test plan

  • Go suite green on develop (transport/call/session, plus race in a container)
  • Client lint/format/vitest/build green (develop CI)
  • Multi-arch image builds and pushes green (develop Docker workflow)
  • Deployed to the staging server; /healthz returns {"status":"ok"}
  • Upstream maintainer review

- session wires nativemlow.WrapEncoder (identity without the tag)
- Dockerfile ARG NATIVE=1 builds opus_mlow (pinned SHA) and links the
  nativemlow encoder statically; NATIVE=0 keeps the pure-Go build
- docker.yml publishes both variants (default tags = native, -pure suffix)
- ci.yml native job builds and tests the tagged variant
- drop per-frame fallback to the stateful Go encoder (state desync risk);
  construction-time fallback only, surfaced by WrapEncoder's mode return
- session logs the ACTIVE encode path, Warn on native degradation
- Dockerfile selects the build stage via VARIANT so pure builds never
  clone or compile the native toolchain
- explicit runtime.KeepAlive across cgo calls
- race-detector coverage for the tagged variant in CI plus a concurrent
  Encode/Close test
feat: optional native MLow encoder via opus_mlow build tag
Redesign session pages into account console with rename support
Surface connected relay as a pill on the identity strip
Dial the FNA relay first on inbound calls
Fail fast with a clear message when the microphone is unavailable
Add call mute with mute_v2 signaling both ways
jobasfernandes and others added 27 commits July 17, 2026 02:46
Parse voip_settings and log the selected audio codec
Record per-call event timeline to JSONL for support
Survive a browser refresh during an active call
Reorder inbound audio through a jitter buffer with concealment
feat: detect the external public ip via stun in the doctor
- fan out terminate reason=accepted_elsewhere to the callee devices that did not answer, using the device list enumerated for the offer
- first accept wins: a later accept from another device no longer swaps acceptedByJid and rekeys srtp mid-call
- a late reject/terminate from a non-answering device no longer tears down the live call
the first-accept guard now only blocks accepts from sibling devices; a retransmitted accept from the answering device reaches the decrypt/rekey block again, which is the only repair path when the first accept's call key failed to decrypt on a transient signal-session desync. the accepted_elsewhere fanout stays one-shot.
…ion-accept

fix: stop sibling devices ringing when a companion answers
- classify by rtp version bits + second byte in the rtcp type range (192-223) instead of exact first bytes: an rtcp compound with more than one report block (0x82+) was misrouted to the rtp path, and an rtp packet with the marker bit and no extension (0x80) was swallowed by the rtcp path
- the peer subscription bootstrap now runs only after srtp auth succeeds, so rtp-shaped bytes with a spoofed ssrc can no longer redirect the subscription (the old classifier shielded one such shape by accident; the invariant is now explicit)
fix: demux rtp/rtcp by version and packet type range
Drop stale call offers replayed after offline reconnect
Each OfflineSyncPreview arms a 30s AfterFunc backstop that is never
cancelled — not by OfflineSyncCompleted, nor by a newer Preview. With
reconnects in quick succession (unstable network, exactly when WhatsApp
replays buffered offers), the first reconnect's orphaned timer fires
mid-way through the second reconnect's replay window and clears
offlineReplaying early, reopening the ghost-ring hole for buffered
offers younger than staleOfferThreshold.

Tag each window with an atomic generation counter and have the backstop
closure only clear the flag if its generation is still current. Keeps
the lock-free atomic.Bool design; isStaleOffer itself is untouched.

Fixes the edge case reported in JotaDev66#43.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fix(session): guard replay-window backstop with a generation token
maxOpenRelays closed relays by OnOpen race order ("closing loser"), which
could shut the is_fna relay carrying the peer's inbound media when it
opened third. prioritizeFNA only guards the dial order, not OnOpen. Keep
maxDialRelays=3 so the dialed set stays bounded.
fix(transport): remove maxOpenRelays cap to keep the is_fna relay open
@JotaDev66
JotaDev66 merged commit d16a076 into JotaDev66:develop Jul 27, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants