cat / goq
clone

turns a linux box into a private steam streaming appliance: plug in, tap your key, play from another computer

fix/architecture-review-findings
goq / scripts / loopback-proof.sh
loopback-proof.sh 22.2K executable
1#!/usr/bin/env bash23set -euo pipefail45script_dir="$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd)"6workspace_dir="$(CDPATH='' cd -- "$script_dir/.." && pwd)"78profile="debug"9primary_frames=60010reconnect_cycles=311reconnect_frames=3012probe_timeout_seconds=1513command_timeout_seconds=4514readiness_timeout_seconds=3015host_runtime_seconds=12016media_transport="iroh-moq"17media_alpn="moq-lite-04"18media_accept_log="authorized MoQ media attachment accepted"19media_release_log="MoQ control client released"20keyframe_request_log="accepted MoQ keyframe request"21session_gate_name="control"22keyframe_recovery="ok"23media_v3=false24relay_only=false25expected_path_mode=direct2627usage() {28 cat <<'EOF'29Usage: scripts/loopback-proof.sh [options]3031Build and exercise the exact sigil and sigil-probe binaries over real loopback32Iroh and upstream MoQ connections. The ordinary proof covers a configured IDR,33native group bounds and recovery, input acknowledgment, single-active-client34rejection, and clean reconnects.3536Options:37 --profile debug|release Cargo profile to build (default: debug)38 --primary-frames COUNT Frames in the held primary session (default: 600)39 --reconnect-cycles COUNT Fresh sessions after the primary (default: 3)40 --reconnect-frames COUNT Frames per reconnect session (default: 30)41 --probe-timeout-seconds SEC Probe per-operation timeout (default: 15)42 --media-v3 Exercise custom grouped media v3 compatibility43 instead of upstream MoQ44 --relay-only Remove direct probe transports and require the45 control, media, and input paths to stay relayed46 --help Show this help47EOF48}4950die() {51 printf 'loopback proof failed: %s\n' "$*" >&252 exit 153}5455require_positive_integer() {56 local name="$1"57 local value="$2"58 case "$value" in59 ''|*[!0-9]*) die "$name must be a positive integer" ;;60 esac61 if [[ "$value" -eq 0 ]]; then62 die "$name must be greater than zero"63 fi64}6566while [[ $# -gt 0 ]]; do67 case "$1" in68 --profile)69 [[ $# -ge 2 ]] || die "--profile requires a value"70 profile="$2"71 shift 272 ;;73 --primary-frames)74 [[ $# -ge 2 ]] || die "--primary-frames requires a value"75 primary_frames="$2"76 shift 277 ;;78 --reconnect-cycles)79 [[ $# -ge 2 ]] || die "--reconnect-cycles requires a value"80 reconnect_cycles="$2"81 shift 282 ;;83 --reconnect-frames)84 [[ $# -ge 2 ]] || die "--reconnect-frames requires a value"85 reconnect_frames="$2"86 shift 287 ;;88 --probe-timeout-seconds)89 [[ $# -ge 2 ]] || die "--probe-timeout-seconds requires a value"90 probe_timeout_seconds="$2"91 shift 292 ;;93 --relay-only)94 relay_only=true95 expected_path_mode=relay96 shift97 ;;98 --media-v3)99 media_transport="grouped-v3"100 media_alpn="sigil/media/3"101 media_accept_log="media v3 client accepted"102 media_release_log="media v3 client released"103 keyframe_request_log="accepted media v3 keyframe request"104 session_gate_name="media"105 media_v3=true106 shift107 ;;108 --help|-h)109 usage110 exit 0111 ;;112 *) die "unknown argument: $1" ;;113 esac114done115116case "$profile" in117 debug)118 cargo_profile="dev"119 profile_dir="debug"120 ;;121 release)122 cargo_profile="release"123 profile_dir="release"124 ;;125 *) die "--profile must be debug or release" ;;126esac127require_positive_integer "--primary-frames" "$primary_frames"128require_positive_integer "--reconnect-cycles" "$reconnect_cycles"129require_positive_integer "--reconnect-frames" "$reconnect_frames"130require_positive_integer "--probe-timeout-seconds" "$probe_timeout_seconds"131[[ "$primary_frames" -ge 4 ]] \132 || die "--primary-frames must be at least 4 for keyframe recovery"133[[ "$reconnect_frames" -ge 4 ]] \134 || die "--reconnect-frames must be at least 4 for keyframe recovery"135136# A fresh Iroh endpoint performs relay discovery and path establishment on137# every reconnect. Keep the default three-cycle proof fast, but scale the host138# watchdog for deliberate soak runs such as --reconnect-cycles 100. The bound139# remains finite and deliberately conservative at eight seconds per cycle.140estimated_primary_seconds=$(( (primary_frames + 59) / 60 ))141scaled_host_runtime_seconds=$((120 + estimated_primary_seconds + reconnect_cycles * 8))142if [[ "$scaled_host_runtime_seconds" -gt "$host_runtime_seconds" ]]; then143 host_runtime_seconds="$scaled_host_runtime_seconds"144fi145146if [[ -f "${HOME}/.cargo/env" ]]; then147 # The project pins its Rust toolchain, but Cargo may not yet be on PATH in a148 # non-interactive shell.149 # shellcheck source=/dev/null150 source "${HOME}/.cargo/env"151fi152153command -v cargo >/dev/null 2>&1 || die "cargo is required"154ffmpeg_bin="$(command -v ffmpeg || true)"155[[ -n "$ffmpeg_bin" ]] || die "ffmpeg is required"156if ! "$ffmpeg_bin" -hide_banner -encoders 2>/dev/null | grep -q '[[:space:]]libx264[[:space:]]'; then157 die "ffmpeg does not provide the required libx264 encoder"158fi159160tmp_root="$(mktemp -d "${TMPDIR:-/tmp}/sigil-loopback.XXXXXX")"161case "$tmp_root" in162 */sigil-loopback.??????) ;;163 *) die "mktemp returned an unexpected path: $tmp_root" ;;164esac165166host_pid=""167host_watchdog_pid=""168primary_pid=""169primary_watchdog_pid=""170bounded_pid=""171bounded_watchdog_pid=""172watchdog_pid=""173174stop_pid() {175 local pid="$1"176 local killer_pid177 if [[ -n "$pid" ]] && kill -0 "$pid" 2>/dev/null; then178 kill -TERM "$pid" 2>/dev/null || true179 (180 for _ in {1..50}; do181 if ! kill -0 "$pid" 2>/dev/null; then182 exit 0183 fi184 sleep 0.1185 done186 kill -KILL "$pid" 2>/dev/null || true187 ) >/dev/null 2>&1 &188 killer_pid=$!189 else190 killer_pid=""191 fi192 if [[ -n "$pid" ]]; then193 wait "$pid" 2>/dev/null || true194 fi195 if [[ -n "$killer_pid" ]]; then196 wait "$killer_pid" 2>/dev/null || true197 fi198}199200cleanup() {201 local exit_status=$?202 trap - EXIT INT TERM HUP203204 stop_pid "$bounded_pid"205 [[ -z "$bounded_watchdog_pid" ]] || wait "$bounded_watchdog_pid" 2>/dev/null || true206 stop_pid "$primary_pid"207 [[ -z "$primary_watchdog_pid" ]] || wait "$primary_watchdog_pid" 2>/dev/null || true208 stop_pid "$host_pid"209 [[ -z "$host_watchdog_pid" ]] || wait "$host_watchdog_pid" 2>/dev/null || true210211 case "$tmp_root" in212 */sigil-loopback.??????) rm -rf -- "$tmp_root" ;;213 esac214 exit "$exit_status"215}216trap cleanup EXIT INT TERM HUP217218sha256_file() {219 local path="$1"220 if command -v sha256sum >/dev/null 2>&1; then221 sha256sum "$path" | awk '{print $1}'222 else223 shasum -a 256 "$path" | awk '{print $1}'224 fi225}226227wait_for_log_line() {228 local log_path="$1"229 local literal="$2"230 local watched_pid="$3"231 local label="$4"232 local deadline=$((SECONDS + readiness_timeout_seconds))233234 while (( SECONDS < deadline )); do235 if grep -Fq -- "$literal" "$log_path" 2>/dev/null; then236 return 0237 fi238 if ! kill -0 "$watched_pid" 2>/dev/null; then239 printf '%s exited before %s readiness. Log follows:\n' "$label" "$literal" >&2240 sed -n '1,240p' "$log_path" >&2 || true241 return 1242 fi243 sleep 0.1244 done245 printf 'timed out waiting for %s readiness marker %s. Log follows:\n' "$label" "$literal" >&2246 sed -n '1,240p' "$log_path" >&2 || true247 return 1248}249250wait_for_log_count() {251 local log_path="$1"252 local literal="$2"253 local expected_count="$3"254 local watched_pid="$4"255 local label="$5"256 local deadline=$((SECONDS + readiness_timeout_seconds))257 local actual_count258259 while (( SECONDS < deadline )); do260 actual_count="$(grep -Fc -- "$literal" "$log_path" 2>/dev/null || true)"261 if [[ "$actual_count" -ge "$expected_count" ]]; then262 return 0263 fi264 if ! kill -0 "$watched_pid" 2>/dev/null; then265 printf '%s exited while waiting for %s occurrence %s. Log follows:\n' \266 "$label" "$literal" "$expected_count" >&2267 sed -n '1,240p' "$log_path" >&2 || true268 return 1269 fi270 sleep 0.1271 done272 printf 'timed out waiting for %s occurrence %s in %s. Log follows:\n' \273 "$literal" "$expected_count" "$label" >&2274 sed -n '1,240p' "$log_path" >&2 || true275 return 1276}277278host_accepted_keyframe_request() {279 local log_path="$1"280 local request_id="$2"281 sed $'s/\033\\[[0-9;]*m//g' "$log_path" 2>/dev/null \282 | awk -v request_id="$request_id" -v request_log="$keyframe_request_log" '283 index($0, request_log) \284 && $0 ~ ("request_id=" request_id "([^[:digit:]]|$)") \285 && index($0, "coalesced=false") { found=1 }286 END { exit !found }287 '288}289290host_keyframe_request_count() {291 local log_path="$1"292 local request_id="$2"293 sed $'s/\033\\[[0-9;]*m//g' "$log_path" 2>/dev/null \294 | awk -v request_id="$request_id" -v request_log="$keyframe_request_log" '295 index($0, request_log) \296 && $0 ~ ("request_id=" request_id "([^[:digit:]]|$)") \297 && index($0, "coalesced=false") { count++ }298 END { print count + 0 }299 '300}301302wait_for_keyframe_request() {303 local log_path="$1"304 local request_id="$2"305 local watched_pid="$3"306 local label="$4"307 local deadline=$((SECONDS + readiness_timeout_seconds))308309 while (( SECONDS < deadline )); do310 if host_accepted_keyframe_request "$log_path" "$request_id"; then311 return 0312 fi313 if ! kill -0 "$watched_pid" 2>/dev/null; then314 printf '%s exited before accepting keyframe request %s. Log follows:\n' \315 "$label" "$request_id" >&2316 sed -n '1,240p' "$log_path" >&2 || true317 return 1318 fi319 sleep 0.1320 done321 printf 'timed out waiting for %s to accept keyframe request %s. Log follows:\n' \322 "$label" "$request_id" >&2323 sed -n '1,240p' "$log_path" >&2 || true324 return 1325}326327start_watchdog() {328 local pid="$1"329 local seconds="$2"330 local timeout_marker="$3"331 (332 local deadline=$((SECONDS + seconds))333 while kill -0 "$pid" 2>/dev/null; do334 if (( SECONDS >= deadline )); then335 : > "$timeout_marker"336 kill -TERM "$pid" 2>/dev/null || true337 for _ in {1..20}; do338 if ! kill -0 "$pid" 2>/dev/null; then339 exit 0340 fi341 sleep 0.1342 done343 kill -KILL "$pid" 2>/dev/null || true344 exit 0345 fi346 sleep 0.1347 done348 ) >/dev/null 2>&1 &349 watchdog_pid=$!350}351352run_bounded() {353 local seconds="$1"354 local output="$2"355 shift 2356 local status357358 rm -f -- "${output}.timeout"359 "$@" >"$output" 2>&1 &360 bounded_pid=$!361 start_watchdog "$bounded_pid" "$seconds" "${output}.timeout"362 bounded_watchdog_pid="$watchdog_pid"363 if wait "$bounded_pid"; then364 status=0365 else366 status=$?367 fi368 bounded_pid=""369 wait "$bounded_watchdog_pid" 2>/dev/null || true370 bounded_watchdog_pid=""371 if [[ -e "${output}.timeout" ]]; then372 return 124373 fi374 return "$status"375}376377validate_probe_log() {378 local log_path="$1"379 local expected_frames="$2"380 local expected_request_id="$3"381 grep -Fxq 'probe=ok' "$log_path" || return 1382 grep -Fxq "frames=${expected_frames}" "$log_path" || return 1383 grep -Fxq 'dimensions=1280x800' "$log_path" || return 1384 grep -Fxq 'sequence_gaps=0' "$log_path" || return 1385 grep -Fxq "transport=${media_transport}" "$log_path" || return 1386 grep -Fxq "transport_alpn=${media_alpn}" "$log_path" || return 1387 grep -Fxq 'first_configured_idr=ok' "$log_path" || return 1388 grep -Fxq 'frame_sequence=monotonic' "$log_path" || return 1389 grep -Fxq "keyframe_recovery=${keyframe_recovery}" "$log_path" || return 1390 grep -Fxq "keyframe_request_id=${expected_request_id}" "$log_path" || return 1391 if [[ "$expected_request_id" == not-requested ]]; then392 grep -Fxq 'keyframe_recovery_micros=not-requested' "$log_path" || return 1393 else394 awk -F= '$1 == "keyframe_recovery_micros" && $2 ~ /^[0-9]+$/ { found=1 } END { exit !found }' \395 "$log_path" || return 1396 fi397 awk -F= '$1 == "media_objects_dropped" && $2 ~ /^[0-9]+$/ { found=1 } END { exit !found }' \398 "$log_path" || return 1399 awk -F= '$1 == "media_objects_late" && $2 ~ /^[0-9]+$/ { found=1 } END { exit !found }' \400 "$log_path" || return 1401 awk -F= '$1 == "keyframes" && $2 ~ /^[0-9]+$/ && $2 > 0 { found=1 } END { exit !found }' \402 "$log_path" || return 1403 awk -F= '$1 == "input_ack_micros" && $2 ~ /^[0-9]+$/ { found=1 } END { exit !found }' \404 "$log_path" || return 1405 grep -Fxq "path_mode=${expected_path_mode}" "$log_path" || return 1406 grep -Fxq "media_path_mode=${expected_path_mode}" "$log_path" || return 1407 grep -Fxq "input_path_mode=${expected_path_mode}" "$log_path" || return 1408 if [[ "$media_transport" == "iroh-moq" ]]; then409 grep -Fxq "control_path_mode=${expected_path_mode}" "$log_path" || return 1410 grep -Fxq 'control_alpn=sigil/control/1' "$log_path" || return 1411 grep -Fxq 'group_sequence=monotonic' "$log_path" || return 1412 grep -Fxq 'moq_group_capacity=1' "$log_path" || return 1413 grep -Fxq 'moq_catalog=goq-v1' "$log_path" || return 1414 grep -Fxq 'moq_unrecovered_group_gaps=0' "$log_path" || return 1415 grep -Fxq 'moq_historical_suffix_frames=0' "$log_path" || return 1416 grep -Fxq 'recovery_barrier=configured-idr' "$log_path" || return 1417 awk -F= '$1 == "moq_group_gaps" && $2 ~ /^[0-9]+$/ { found=1 } END { exit !found }' \418 "$log_path" || return 1419 awk -F= '$1 == "moq_cancelled_groups" && $2 ~ /^[0-9]+$/ && $2 > 0 { found=1 } END { exit !found }' \420 "$log_path" || return 1421 awk -F= '$1 == "moq_maximum_group_objects" && $2 ~ /^[0-9]+$/ && $2 > 0 && $2 <= 256 { found=1 } END { exit !found }' \422 "$log_path" || return 1423 awk -F= '$1 == "moq_maximum_group_bytes" && $2 ~ /^[0-9]+$/ && $2 > 0 && $2 <= 33554432 { found=1 } END { exit !found }' \424 "$log_path" || return 1425 fi426 if [[ "$media_transport" == "iroh-moq" && "$expected_request_id" == 1 ]]; then427 grep -Fxq 'slow_consumer=ok' "$log_path" || return 1428 grep -Fxq 'slow_consumer_stall_ms=1500' "$log_path" || return 1429 grep -Fxq 'slow_consumer_first_post_stall=configured-idr' "$log_path" || return 1430 grep -Fxq 'slow_consumer_historical_suffix_frames=0' "$log_path" || return 1431 awk -F= '$1 == "slow_consumer_recovery_micros" && $2 ~ /^[0-9]+$/ && $2 <= 2000000 { found=1 } END { exit !found }' \432 "$log_path" || return 1433 awk -F= '$1 == "slow_consumer_cancellation_delta" && $2 ~ /^[0-9]+$/ && $2 > 0 { found=1 } END { exit !found }' \434 "$log_path" || return 1435 awk -F= '$1 == "slow_consumer_group_advance" && $2 ~ /^[0-9]+$/ && $2 > 0 { found=1 } END { exit !found }' \436 "$log_path" || return 1437 awk -F= '$1 == "slow_consumer_sequence_advance" && $2 ~ /^[0-9]+$/ && $2 > 0 { found=1 } END { exit !found }' \438 "$log_path" || return 1439 awk -F= '$1 == "slow_consumer_capture_advance_micros" && $2 ~ /^[0-9]+$/ && $2 >= 750000 { found=1 } END { exit !found }' \440 "$log_path" || return 1441 awk -F= '$1 == "slow_consumer_input_ack_micros" && $2 ~ /^[0-9]+$/ && $2 <= 2000000 { found=1 } END { exit !found }' \442 "$log_path" || return 1443 else444 grep -Fxq 'slow_consumer=not-requested' "$log_path" || return 1445 fi446 grep -Fxq "forced_relay=$([[ "$relay_only" == true ]] && printf ok || printf not-requested)" \447 "$log_path" || return 1448}449450run_probe() {451 if [[ "$relay_only" == true ]]; then452 set -- --relay-only "$@"453 fi454 if [[ "$media_v3" == true ]]; then455 "$probe_bin" --media-v3 "$@"456 else457 "$probe_bin" "$@"458 fi459}460461run_proof_probe() {462 local request_id="$1"463 shift464 run_probe --keyframe-smoke --keyframe-request-id "$request_id" "$@"465}466467cd "$workspace_dir"468cargo build --locked --profile "$cargo_profile" -p sigil-host --bins469470target_root="${CARGO_TARGET_DIR:-$workspace_dir/target}"471case "$target_root" in472 /*) ;;473 *) target_root="$workspace_dir/$target_root" ;;474esac475host_bin="$target_root/$profile_dir/sigil"476probe_bin="$target_root/$profile_dir/sigil-probe"477[[ -x "$host_bin" ]] || die "built host binary is missing: $host_bin"478[[ -x "$probe_bin" ]] || die "built probe binary is missing: $probe_bin"479480identity_path="$tmp_root/host.key"481runtime_path="$tmp_root/runtime"482identity_log="$tmp_root/identity.log"483host_log="$tmp_root/host.log"484primary_log="$tmp_root/primary.log"485secondary_log="$tmp_root/secondary.log"486487"$host_bin" identity init --output "$identity_path" >"$identity_log" 2>&1488node_id="$(sed -n 's/^node_id=//p' "$identity_log" | tail -n 1)"489[[ -n "$node_id" ]] || die "identity initialization did not print a node ID"490491RUST_LOG='info,sigil::server=debug' "$host_bin" serve \492 --identity "$identity_path" \493 --source test-pattern \494 --state-path "$runtime_path" \495 --width 1280 \496 --height 800 \497 --framerate 60 \498 --ffmpeg "$ffmpeg_bin" \499 >"$host_log" 2>&1 &500host_pid=$!501start_watchdog "$host_pid" "$host_runtime_seconds" "$tmp_root/host.timeout"502host_watchdog_pid="$watchdog_pid"503wait_for_log_line "$host_log" 'status=ready' "$host_pid" 'sigil' || die "host did not become ready"504host_node_id="$(sed -n 's/^node_id=//p' "$host_log" | tail -n 1)"505[[ "$host_node_id" == "$node_id" ]] || die "ready host node ID does not match its identity"506507primary_probe_args=(508 --node-id "$node_id"509 --frames "$primary_frames"510 --timeout-seconds "$probe_timeout_seconds"511 --expect-size 1280x800512)513if [[ "$media_v3" == false ]]; then514 primary_probe_args+=(--slow-consumer-ms 1500)515fi516run_proof_probe 1 "${primary_probe_args[@]}" >"$primary_log" 2>&1 &517primary_pid=$!518start_watchdog "$primary_pid" "$command_timeout_seconds" "$tmp_root/primary.timeout"519primary_watchdog_pid="$watchdog_pid"520521if ! wait_for_log_count "$host_log" "$media_accept_log" 1 "$primary_pid" 'primary probe'; then522 sed -n '1,240p' "$primary_log" >&2 || true523 die "primary session did not become active"524fi525if ! kill -0 "$primary_pid" 2>/dev/null; then526 die "primary probe ended before the concurrent rejection check"527fi528529if run_bounded "$probe_timeout_seconds" "$secondary_log" \530 run_probe --node-id "$node_id" --frames 1 \531 --timeout-seconds "$probe_timeout_seconds" --expect-size 1280x800; then532 die "secondary client was accepted while the primary was active"533fi534if [[ -e "${secondary_log}.timeout" ]]; then535 die "secondary client timed out instead of receiving the active-client rejection"536fi537if ! grep -Fxq "Error: host rejected ${session_gate_name} stream: host already has an active client" "$secondary_log"; then538 printf 'secondary probe did not return the expected rejection. Log follows:\n' >&2539 sed -n '1,120p' "$secondary_log" >&2 || true540 die "unexpected secondary-client failure"541fi542543if wait "$primary_pid"; then544 primary_status=0545else546 primary_status=$?547fi548primary_pid=""549wait "$primary_watchdog_pid" 2>/dev/null || true550primary_watchdog_pid=""551[[ ! -e "$tmp_root/primary.timeout" ]] || die "primary probe exceeded its deadline"552[[ "$primary_status" -eq 0 ]] || {553 sed -n '1,240p' "$primary_log" >&2 || true554 die "primary probe exited with status $primary_status"555}556validate_probe_log "$primary_log" "$primary_frames" 1 || {557 sed -n '1,240p' "$primary_log" >&2 || true558 die "primary probe evidence is incomplete"559}560wait_for_keyframe_request "$host_log" 1 "$host_pid" 'sigil' \561 || die "primary keyframe request was not accepted by the host"562563wait_for_log_count "$host_log" "$media_release_log" 1 "$host_pid" 'sigil' \564 || die "primary session was not released"565wait_for_log_count "$host_log" 'input client released' 1 "$host_pid" 'sigil' \566 || die "primary input session was not drained"567568cycle=1569while [[ "$cycle" -le "$reconnect_cycles" ]]; do570 reconnect_log="$tmp_root/reconnect-${cycle}.log"571 reconnect_request_id=$((cycle + 1))572 if ! run_bounded "$command_timeout_seconds" "$reconnect_log" \573 run_proof_probe "$reconnect_request_id" --node-id "$node_id" --frames "$reconnect_frames" \574 --timeout-seconds "$probe_timeout_seconds" --expect-size 1280x800; then575 sed -n '1,240p' "$reconnect_log" >&2 || true576 die "reconnect cycle $cycle failed"577 fi578 validate_probe_log "$reconnect_log" "$reconnect_frames" "$reconnect_request_id" || {579 sed -n '1,240p' "$reconnect_log" >&2 || true580 die "reconnect cycle $cycle evidence is incomplete"581 }582 wait_for_keyframe_request "$host_log" "$reconnect_request_id" "$host_pid" 'sigil' \583 || die "reconnect cycle $cycle keyframe request was not accepted by the host"584 wait_for_log_count "$host_log" "$media_release_log" "$((cycle + 1))" "$host_pid" 'sigil' \585 || die "reconnect cycle $cycle was not released"586 wait_for_log_count "$host_log" 'input client released' "$((cycle + 1))" "$host_pid" 'sigil' \587 || die "reconnect input cycle $cycle was not drained"588 cycle=$((cycle + 1))589done590591request_id=1592while [[ "$request_id" -le $((reconnect_cycles + 1)) ]]; do593 request_count="$(host_keyframe_request_count "$host_log" "$request_id")"594 [[ "$request_count" -eq 1 ]] \595 || die "keyframe request $request_id was accepted $request_count times; expected exactly once"596 request_id=$((request_id + 1))597done598599kill -TERM "$host_pid" 2>/dev/null || true600if wait "$host_pid"; then601 host_status=0602else603 host_status=$?604fi605host_pid=""606wait "$host_watchdog_pid" 2>/dev/null || true607host_watchdog_pid=""608[[ ! -e "$tmp_root/host.timeout" ]] || die "host exceeded its bounded runtime"609[[ "$host_status" -eq 0 ]] || {610 sed -n '1,240p' "$host_log" >&2 || true611 die "host exited with status $host_status"612}613grep -Fq 'shutdown signal received' "$host_log" || die "host did not record a graceful shutdown"614615printf 'loopback_proof=ok\n'616printf 'profile=%s\n' "$profile"617printf 'host_binary=%s\n' "$host_bin"618printf 'host_sha256=%s\n' "$(sha256_file "$host_bin")"619printf 'probe_binary=%s\n' "$probe_bin"620printf 'probe_sha256=%s\n' "$(sha256_file "$probe_bin")"621printf 'node_id=%s\n' "$node_id"622printf 'primary_frames=%s\n' "$primary_frames"623grep -E '^(transport|control_alpn|transport_alpn|first_configured_idr|frame_sequence|group_sequence|keyframe_recovery|keyframe_recovery_micros|keyframe_request_id|keyframes|sequence_gaps|media_objects_dropped|media_objects_late|moq_catalog|moq_group_capacity|moq_cancelled_groups|moq_group_gaps|moq_unrecovered_group_gaps|moq_maximum_group_objects|moq_maximum_group_bytes|moq_historical_suffix_frames|recovery_barrier|slow_consumer|slow_consumer_stall_ms|slow_consumer_first_post_stall|slow_consumer_historical_suffix_frames|slow_consumer_recovery_micros|slow_consumer_cancellation_delta|slow_consumer_group_advance|slow_consumer_sequence_advance|slow_consumer_capture_advance_micros|slow_consumer_input_ack_micros|input_ack_micros|path_mode|path_rtt_ms|control_path_mode|control_path_rtt_ms|media_path_mode|media_path_rtt_ms|input_path_mode|input_path_rtt_ms|forced_relay)=' "$primary_log"624printf 'keyframe_request_correlation=unique\n'625printf 'active_client_rejection=ok\n'626printf 'reconnect_cycles=%s\n' "$reconnect_cycles"627printf 'cleanup=ok\n'