Skip to content

spike(ISP-049): test COEP vs TURN relay, fix blocked-autoplay audio, record findings (partial #719) - #848

Merged
Mikey-222 merged 2 commits into
Hel-Phone:mainfrom
praizehimm:spike/isp-049-webrtc-coep-audio
Oct 20, 2026
Merged

Mikey-222 merged 2 commits into
Hel-Phone:mainfrom
praizehimm:spike/isp-049-webrtc-coep-audio

Conversation

@praizehimm

@praizehimm praizehimm commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

What I found when checking the issue against the code

Details and commands are in docs/spikes/719-webrtc-coep-turn-audio-report.md. The important points:

  • There is no TURN server in this repo. No turn: URL, no turn-credentials route (the path doesn't exist), no coturn config. src/lib/webrtc.ts is a 27-line STUN-only ICE benchmark, with iceCandidatePoolSize: 2 (not 10).
  • server/middleware/csp.ts emits no COOP/COEP. They are set in vite.config.ts (dev and preview) and referenced by test/coep-headers-spike.test.js.
  • iceConnectionState isn't used anywhere, there is no Web Push code, and wsClient.ts already has exponential backoff with jitter.
  • test/audio-alert.test.js was failing on main, not green: vitest 4 can't new an arrow-function mock.

1. Experiment: does COOP/COEP block TURN relay? (it doesn't, over UDP)

scripts/spikes/webrtc_coep_turn_experiment.mjs runs a real TURN server and, in Chrome 155, opens relay-only peer connections (iceTransportPolicy: 'relay'), so a data channel opens only if the TURN allocation works. 10 runs per row:

Headers TURN/UDP connected via relay pair
none (control) 10/10 10/10
COOP same-origin + COEP credentialless (the repo's config), crossOriginIsolated=true 10/10 10/10
COOP same-origin + COEP require-corp, crossOriginIsolated=true 10/10 10/10

So the issue's central diagnosis ("COEP blocks TURN allocation") is not supported: ICE/TURN traffic is not an HTTP fetch, so COEP/CORP don't apply to it. If calls fail in production, the cause is more likely that no TURN is configured, or that the credential fetch() is cross-origin.

Honest limits:

  • The TCP rows (0/10) are inconclusive: they fail in the no-headers control too, so that is my local node-turn/Chrome TCP setup, not the headers.
  • One machine, one browser, loopback only. It does not emulate carrier NATs, other browsers, or turns: (TLS).
  • The script needs npm i --no-save node-turn (not added to package.json) and a Chrome (--executable / CHROMIUM_PATH, like the other spike scripts).

2. Fix: EmergencyAudioAlert hung silently when autoplay was blocked

play() awaited AudioContext.resume(), which stays pending forever when the autoplay policy blocks audio. A siren with no recent user gesture hung with no sound and no record; unlock() existed but was never called.

  • play() waits at most 1 s, then vibrates ([500,250,500], best effort) and returns 'blocked' (else 'played').
  • unlock() reports whether audio is running; new armOnFirstGesture() unlocks on the first user gesture (Help.tsx arms it on mount).
  • New trackAudioAlert('played'|'blocked') in telemetry.ts emits an audio.alert span, so the blocked rate is measurable; getStats() exposes counts.
  • The "Test siren" button now says when sound is blocked.

Tests: test/audio-alert.test.js is 10 tests (it was 2, both failing). 9 of the 10 fail against the old audioAlert.ts, including the hang case. tsc --noEmit error count is unchanged (29 before and after, none in the changed files). ESLint can't parse any .ts in this repo (pre-existing), so it gave no signal here.

Not done (needs a TURN deployment, real devices, or product decisions)

Per-call TURN credentials/proxy (nothing to proxy yet) · Web Push wake path · NAT×battery×network policy table and the Moto G measurements · Playwright webrtc-nat / background-kill projects · ice* / turn401 / wsReaped / pushDelivered metrics, dashboards and alerts · NAT-mix peers in server/tests/load.js · rejoin allowlist in rateLimiter.ts, keepAlive timing, crashRecovery.ts (not examined) · the ops runbook. I'd suggest splitting these into separate issues now that the COEP premise is checked.

Commits

  1. fix(audio): stop EmergencyAudioAlert hanging when autoplay is blocked
  2. docs(spike): test whether COOP/COEP blocks TURN relay; record #719 findings

I did not run the full test suite or the Playwright projects.
Closes #719
Closes #711
Closes #717
Closes #718

play() awaited AudioContext.resume(), which stays pending forever when the
autoplay policy blocks audio, so a siren with no recent user gesture hung
silently and recorded nothing; unlock() was never called anywhere.

- play() gives up after RESUME_TIMEOUT_MS, vibrates as a fallback and
  returns 'blocked' (or 'played'); unlock() reports whether audio is running
- armOnFirstGesture() unlocks audio on the first user gesture; Help arms it
- trackAudioAlert() emits an audio.alert span (played/blocked) and
  getStats() exposes the counts
- the Test siren button says when sound is blocked

test/audio-alert.test.js was failing on main (vitest 4 cannot 'new' an
arrow-function mock); fix the fake AudioContext and add 8 tests covering
the blocked, rejected, no-vibration, gesture and disposer cases.

Refs Hel-Phone#719
…ne#719 findings

Add a reproducible experiment (real TURN server + relay-only peer
connections in Chrome under no headers, COEP credentialless and COEP
require-corp). Over UDP it connected 10/10 through the relay in every
configuration, so the issue's COEP-blocks-TURN diagnosis is not supported;
TCP failed in the control too and is inconclusive.

The report also checks each claim in the issue against the repository
(no TURN server, no COOP/COEP in csp.ts, 27-line webrtc.ts, reconnect
backoff already present) and lists what remains.

Refs Hel-Phone#719
@drips-wave

drips-wave Bot commented Oct 20, 2026

Copy link
Copy Markdown

@praizehimm Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Mikey-222
Mikey-222 merged commit 72d07de into Hel-Phone:main Oct 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment