Prove the TURN-relayed camera path, the one the browser actually uses

The first e2e-camera.sh connected over direct host candidates. The real
browser is pinned to iceTransportPolicy "relay", and PLAN.md recorded
go2rtc's known trap for that configuration: a relay candidate on one
side only can leave ICE stuck in `checking`.

The script now runs the peer a second time pinned the same way, with the
iceServers the backend hands out. The peer asserts its offer contains
only `typ relay` candidates, so a pass cannot mean it connected directly,
and never prints the credentials. Measured against the H2C: 937 packets
(~943 KB) arrive through Cloudflare, and the trap did not occur.

The phase is skipped with an explicit message when TURN credentials are
absent, so a run without them cannot be mistaken for one that verified
the relay. Both limits are written into the script header: viewer and
gateway share a host, so this is not two distinct NATs, and no real
browser has rendered a frame.
This commit is contained in:
2026-10-24 08:06:58 +00:00
parent ac484a99ba
commit e4940f9404
2 changed files with 81 additions and 20 deletions
+49 -19
View File
@@ -9,13 +9,21 @@
# more than it was because a stray go2rtc left over from an earlier run
# answered the stub with an HTTP 500 and the test still printed "ok".
#
# This one runs the real thing end to end:
# This one runs the real thing end to end, twice - once over direct candidates
# and once pinned to relay-only through Cloudflare TURN, which is how the real
# browser is configured:
#
# werift peer (stands in for the browser)
# -> POST /printers/:id/webrtc on the real backend
# -> /ws/edge/v1 -> the real continuum-proxy -> its real go2rtc
# -> RTSPS to the printer's camera
# and the RTP that comes back is counted.
# and the RTP that comes back is counted. The relay phase needs
# CLOUDFLARE_TURN_KEY_ID / CLOUDFLARE_TURN_API_TOKEN and is skipped, with a
# message saying the TURN path went unverified, when they are absent.
#
# Not covered: the viewer and the gateway share this host, so what is proven is
# that ICE, DTLS and media work via TURN, not that they survive two distinct
# NATs; and a real browser has still not rendered a frame.
#
# SAFETY: read-only against the printers. It sends no printer command of any
# kind; the proxy's MQTT subscription is passive and the camera is a pull of
@@ -205,25 +213,47 @@ ok "the generated go2rtc config is owner-only (mode $mode)"
# --- 4. the viewer ---------------------------------------------------------
echo "==> a real WebRTC peer plays the camera through the relay"
RESULTS="$(BASE="$BASE" PRINTER_UUID="$CAM_UUID" ACCESS_TOKEN="$USER_TOKEN" bun scripts/e2e-camera-peer.ts 2>&1 || true)"
SEEN=0; BAD=0
while IFS=$'\t' read -r verdict label; do
case "$verdict" in
ok) ok "$label"; SEEN=$((SEEN + 1)) ;;
FAILED) echo " FAILED: $label" >&2; SEEN=$((SEEN + 1)); BAD=$((BAD + 1)) ;;
esac
done < <(printf '%s\n' "$RESULTS" | grep -E "^(ok|FAILED)$(printf '\t')")
# Fewer results than the peer's five checks means it died before finishing,
# and a silent peer must not read as a pass.
if [ "$SEEN" -lt 5 ] && [ "$BAD" -eq 0 ]; then
BAD=1; echo " FAILED: the peer produced only $SEEN of 5 results" >&2
fi
if [ "$BAD" -ne 0 ]; then
printf '%s\n' "$RESULTS" | sed 's/^/ | /' >&2
fail "the camera did not deliver video"
# Runs the peer once and reads its `ok<TAB>label` / `FAILED<TAB>label` lines.
# $1 names the phase, $2 is how many results a complete run produces: fewer
# means the peer died before finishing, and a silent peer must not read as a
# pass.
run_viewer() {
local phase="$1" expected="$2" results seen=0 bad=0 verdict label
shift 2
results="$(env BASE="$BASE" PRINTER_UUID="$CAM_UUID" ACCESS_TOKEN="$USER_TOKEN" "$@" \
bun scripts/e2e-camera-peer.ts 2>&1 || true)"
while IFS=$'\t' read -r verdict label; do
case "$verdict" in
ok) ok "$label"; seen=$((seen + 1)) ;;
FAILED) echo " FAILED: $label" >&2; seen=$((seen + 1)); bad=$((bad + 1)) ;;
esac
done < <(printf '%s\n' "$results" | grep -E "^(ok|FAILED)$(printf '\t')")
if [ "$seen" -lt "$expected" ] && [ "$bad" -eq 0 ]; then
bad=1; echo " FAILED: the $phase peer produced only $seen of $expected results" >&2
fi
if [ "$bad" -ne 0 ]; then
printf '%s\n' "$results" | sed 's/^/ | /' >&2
fail "the camera did not deliver video ($phase)"
fi
}
echo "==> a real WebRTC peer plays the camera through the relay (direct candidates)"
run_viewer "direct" 5
# The path the product actually uses: the browser is pinned to relay-only, so
# media goes through Cloudflare TURN. Needs real credentials, which come from
# the environment, so it is skipped - loudly - rather than failed without them.
if [ -n "${CLOUDFLARE_TURN_KEY_ID:-}" ] && [ -n "${CLOUDFLARE_TURN_API_TOKEN:-}" ]; then
echo "==> the same camera, viewer pinned to relay-only through Cloudflare TURN"
run_viewer "relay-only" 7 ICE_MODE=relay
else
echo " SKIPPED: relay-only phase - CLOUDFLARE_TURN_KEY_ID / CLOUDFLARE_TURN_API_TOKEN not set."
echo " The TURN-relayed path, which the browser is pinned to, is NOT verified by this run."
fi
echo
echo "PASS - a real WebRTC peer received video from the printer's camera"
echo " through the backend relay, the uplink, the proxy and go2rtc."
if [ -n "${CLOUDFLARE_TURN_KEY_ID:-}" ] && [ -n "${CLOUDFLARE_TURN_API_TOKEN:-}" ]; then
echo " Both directly and pinned to relay-only through Cloudflare TURN."
fi