diff --git a/cli/tests/marmot/marmot-interop-headless.sh b/cli/tests/marmot/marmot-interop-headless.sh index b754ec64b0..4dc78eb538 100755 --- a/cli/tests/marmot/marmot-interop-headless.sh +++ b/cli/tests/marmot/marmot-interop-headless.sh @@ -61,6 +61,8 @@ BROKER_HOST="${BROKER_HOST:-127.0.0.1}" BROKER_PORT="${BROKER_PORT:-4455}" BROKER_URI="quic://$BROKER_HOST:$BROKER_PORT" BROKER_PID="" +# SHA-256 of the broker's self-signed leaf, read from its startup JSON. +BROKER_PIN="" # A loopback Blossom blob store for the encrypted-media tests. Ciphertext only: # the file key comes from each group's MLS exporter and never reaches it. diff --git a/cli/tests/marmot/setup.sh b/cli/tests/marmot/setup.sh index 88aa4150f6..c5ebb35b36 100644 --- a/cli/tests/marmot/setup.sh +++ b/cli/tests/marmot/setup.sh @@ -143,7 +143,18 @@ start_quic_broker() { local deadline=$(( $(date +%s) + 15 )) while [[ $(date +%s) -lt $deadline ]]; do if grep -q '"local_addr"' "$STATE_DIR/broker/stdout.log" 2>/dev/null; then - info "broker pid $BROKER_PID ready" + # The broker generates a self-signed certificate and prints its + # fingerprint. `amy` pins that exact leaf rather than trusting a chain — + # there is no CA in this picture, and without the pin every stream test + # fails inside TLS before a single frame is written. + BROKER_PIN=$(sed -n 's/.*"server_cert_sha256_fingerprint":"\([0-9a-f]*\)".*/\1/p' \ + "$STATE_DIR/broker/stdout.log" | head -1) + if [[ -z "$BROKER_PIN" ]]; then + fail_msg "broker printed no server_cert_sha256_fingerprint — cannot pin it" + BROKER_PID="" + return 1 + fi + info "broker pid $BROKER_PID ready (cert ${BROKER_PIN:0:16}…)" return 0 fi if ! kill -0 "$BROKER_PID" 2>/dev/null; then break; fi diff --git a/cli/tests/marmot/tests-extras.sh b/cli/tests/marmot/tests-extras.sh index 9df6cb7a34..10dbcb04ee 100644 --- a/cli/tests/marmot/tests-extras.sh +++ b/cli/tests/marmot/tests-extras.sh @@ -456,7 +456,7 @@ test_18_agent_stream_amy_publishes() { local send_json thash chunks send_json=$(amy_json marmot stream send "$gid" --stream-id "$sid" --start-event-id "$seid" \ - --broker "$BROKER_URI" "Hello " "from " "amethyst") || { + --broker "$BROKER_URI" --pin-sha256 "$BROKER_PIN" "Hello " "from " "amethyst") || { record_result "$id" fail "amy stream send failed"; return } thash=$(printf '%s' "$send_json" | jq -r '.transcript_hash // empty') @@ -466,7 +466,8 @@ test_18_agent_stream_amy_publishes() { # Our own subscriber must recover the stream from the broker's replay window # and fold it to the same transcript the publisher computed. local watch_json - watch_json=$(amy_json marmot stream watch "$gid" --stream-id "$sid" --timeout 15) || { + watch_json=$(amy_json marmot stream watch "$gid" --stream-id "$sid" --timeout 15 \ + --pin-sha256 "$BROKER_PIN") || { record_result "$id" fail "amy stream watch failed"; return } printf 'stream18 watch=%s\n' "$watch_json" >>"$LOG_FILE" @@ -535,7 +536,8 @@ test_19_agent_stream_wn_publishes() { fi local watch_out="$STATE_DIR/stream-19-watch.json" - ( amy_a marmot stream watch "$gid" --stream-id "$wsid" --timeout 25 >"$watch_out" 2>>"$LOG_FILE" ) & + ( amy_a marmot stream watch "$gid" --stream-id "$wsid" --timeout 25 \ + --pin-sha256 "$BROKER_PIN" >"$watch_out" 2>>"$LOG_FILE" ) & local watch_pid=$! sleep 4