Files
flamenet-messenger-android/scripts/obfs4-interop.sh
T
Paul HittandClaude Fable 5.1 4dd544083c Remove machine-writing tells from comments and docs
Emoji markers, markdown bold inside code comments, and em-dashes are
gone from source comments, string literals, scripts, Gradle files and
the markdown docs. Counted before and after:

  emoji markers   2680 -> 1 (the emoji picker's own list, which is the feature)
  em-dashes       2490 -> 9 (table placeholders and fenced code)

Two warning glyphs in user-facing strings in ChatScreens.kt are removed
as well; the sentences say the same thing without them. Two backtick
test names carried an em-dash and now carry a comma.

Nothing else changed. 1208 tests pass, counted from the result XML.
The three obfs4 interop tests are opted out with OBFS4_INTEROP_OPTOUT=1
for this run, as their own message instructs when no bridge is set up.

CONTRIBUTING gets a four-line rule so it does not grow back.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 18:52:12 -04:00

92 lines
4.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# Run the Kotlin obfs4 port against real lyrebird, in both directions.
#
# The only check that proves interoperability. Everything else in `:obfs4`
# compares against recorded vectors or against itself, and two identically-wrong
# ends agree perfectly.
#
# A wire-format bug shows up as SILENCE, not an error: obfs4 bridges do not
# answer handshakes they cannot verify. The Kotlin side uses read timeouts and
# reports them as "the bridge refused to answer".
#
# The Go counterparty lives in the flamenet-elligator2 checkout, which also
# holds the Swift port. Both clients are pinned to the SAME reference rather
# than to each other, two independent implementations agreeing with lyrebird is
# a real statement; agreeing with each other is not.
set -euo pipefail
cd "$(dirname "$0")/.."
REFERENCE="${FN_OBFS4_REFERENCE:-../flamenet-elligator2/Reference}"
if [[ ! -d "$REFERENCE" ]]; then
echo "Cannot find the Go reference at $REFERENCE."
echo "Clone flamenet-elligator2 beside this repo, or set FN_OBFS4_REFERENCE."
exit 1
fi
WORK="$(mktemp -d)"
PARAMS="$WORK/params.json"
SERVER_LOG="$WORK/server.log"
cleanup() {
if [[ -n "${SERVER_PID:-}" ]] && kill -0 "$SERVER_PID" 2>/dev/null; then
kill "$SERVER_PID" 2>/dev/null || true
wait "$SERVER_PID" 2>/dev/null || true
fi
# The bridge log is the other half of every diagnosis: it says whether the
# handshake was accepted or refused, which the silent client cannot.
if [[ -s "$SERVER_LOG" ]]; then echo "--- bridge log ---"; cat "$SERVER_LOG"; fi
rm -rf "$WORK"
}
trap cleanup EXIT
echo "building the lyrebird bridge and client..."
(cd "$REFERENCE" && go build -o "$WORK/interopserver" ./interopserver)
(cd "$REFERENCE" && go build -o "$WORK/interopclient" ./interopclient)
"$WORK/interopserver" -params "$PARAMS" -addr 127.0.0.1:0 >/dev/null 2>"$SERVER_LOG" &
SERVER_PID=$!
# Poll for the params file rather than sleeping. A fixed sleep is either
# flaky or slow, and on a loaded machine it is both.
for _ in $(seq 1 100); do
[[ -f "$PARAMS" ]] && break
kill -0 "$SERVER_PID" 2>/dev/null || { echo "the bridge died on startup"; exit 1; }
sleep 0.1
done
[[ -f "$PARAMS" ]] || { echo "the bridge never published its parameters"; exit 1; }
# sed, not python3 or jq: the CI images for this repo cannot be assumed to
# have either, and that assumption has cost red pipelines before.
ADDRESS="$(sed -n 's/.*"address"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$PARAMS")"
[[ -n "$ADDRESS" ]] || { echo "could not read the bridge address from $PARAMS"; exit 1; }
echo "bridge up at $ADDRESS"
# `--rerun-tasks`, because Gradle's up-to-date check does not know the
# counterparty changed. Without it a second run skips the task entirely and
# prints nothing, the tests do not fail, they simply never happen. Caught by
# the name check below, which is the second time that check has earned its keep
# in ten minutes.
OBFS4_INTEROP_PARAMS="$PARAMS" OBFS4_INTEROP_CLIENT="$WORK/interopclient" \
./gradlew :obfs4:test --tests '*InteropTest*' --rerun-tasks --console=plain 2>&1 \
| tee "$WORK/test.log"
# A Gradle filter that matches nothing still exits 0, which would make "no
# interop coverage at all" look exactly like success.
for name in "a handshake and an echo against a real lyrebird bridge" \
"a bridge ignores a handshake it cannot verify" \
"a real lyrebird client accepts the kotlin bridge"; do
# Gradle prints `name() PASSED`, with parentheses. Matching on
# "$name PASSED" silently matches nothing, which this check caught on
# itself, reporting failure while all three tests had passed.
grep -qF "$name() PASSED" "$WORK/test.log" || {
echo
echo "FAILED: '$name' did not pass. A filter that matches nothing exits 0,"
echo "so each test is checked by name rather than trusting the exit code."
exit 1
}
done
echo
echo "ANDROID INTEROP OK: both directions -- a real lyrebird bridge accepted the"
echo "Kotlin client, and a real lyrebird client accepted the Kotlin bridge."