From 62a7fbf695405d8804220c1cf1b280ee8d612082 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Wed, 19 Aug 2026 15:02:32 -0700 Subject: fix(agent-text): relay through a host that holds the account The relay target was hardcoded to velox, and velox's reinstall wiped its signal-cli registration. A send from velox then relayed to itself, and any machine without a local account relayed into the same dead end. Only ratio still worked, because it holds the account as a linked device and sends directly. The old failure text blamed velox for being down. It was up, just unregistered. The target is now an ordered list, overridable per invocation, and the loop skips any candidate matching this machine's name. The skip is the actual fix. Reaching the relay branch means the account isn't local, so relaying to ourselves can't work whatever the list order. Both sides of that comparison are domain-stripped, or an FQDN nodename would silently disable the guard. A relay host that is reachable but unregistered exits 1, not 0, so the loop advances instead of reporting a delivery that never happened. I checked that rather than assume it. Silent non-delivery is the worst failure this tool has, and a test pins it. The failure message now names which branch failed, so a local send failure no longer sends anyone chasing the tailnet. protocols.org and the pager runbook both asserted the topology the reinstall destroyed. They now say how to derive which machine holds the account instead of naming one. The snapshot form is what rotted. The runbook's linking recipe is marked impossible for now, since the primary's keys are gone and a linked device can't authorize another. --- scripts/tests/agent-text.bats | 101 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 92 insertions(+), 9 deletions(-) (limited to 'scripts/tests/agent-text.bats') diff --git a/scripts/tests/agent-text.bats b/scripts/tests/agent-text.bats index e5d80fc..d4c5f9b 100644 --- a/scripts/tests/agent-text.bats +++ b/scripts/tests/agent-text.bats @@ -1,11 +1,14 @@ #!/usr/bin/env bats # agent-text — the runtime-neutral Signal phone messenger ("text me"). Reaches # Craig over Signal from any machine on the tailnet: sends directly wherever the -# account is registered locally (velox, or any linked device), and ssh-relays to -# velox from a machine that doesn't hold the account. These tests stub -# ssh/signal-cli on PATH to verify command construction without a network or a -# phone. The signal-cli stub answers `listAccounts` to control which branch the -# dispatch takes. The final test covers the deprecated agent-page shim. +# account is registered locally (any machine holding it, as primary or as a +# linked device), and otherwise ssh-relays to the first relay host that holds +# it. These tests stub ssh/signal-cli/uname on PATH to verify command +# construction without a network or a phone. The signal-cli stub answers +# `listAccounts` to control which branch the dispatch takes; the uname stub +# fixes the machine identity, so the self-relay skip is exercised deterministically +# rather than depending on which machine runs the suite. The final test covers +# the deprecated agent-page shim. setup() { REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../.." && pwd)" @@ -29,7 +32,14 @@ fi echo "signal-cli \$*" >> "$LOG" exit 0 EOF - chmod +x "$STUBS/ssh" "$STUBS/signal-cli" + # SELF_HOST fixes what the script sees as this machine. Defaults to a name + # in neither relay list, so the plain relay tests reach the first host. + cat > "$STUBS/uname" < "$STUBS/ssh" <> "$LOG" +case "\$*" in *ratio*) exit 255 ;; esac +exit 0 +EOF + chmod +x "$STUBS/ssh" + HAS_ACCOUNT=0 PATH="$STUBS:$PATH" run bash "$PAGE" retry me + [ "$status" -eq 0 ] + grep -q "^ssh .*ratio" "$LOG" + grep -q "^ssh .*velox" "$LOG" +} + +@test "AGENT_TEXT_RELAYS overrides the default relay list" { + AGENT_TEXT_RELAYS="spare.example.net" HAS_ACCOUNT=0 PATH="$STUBS:$PATH" \ + run bash "$PAGE" custom + [ "$status" -eq 0 ] + grep -q "^ssh .*spare.example.net" "$LOG" + ! grep -q "^ssh .*ratio" "$LOG" } @test "sends directly (no ssh) when the pager account is registered locally" { @@ -59,7 +114,7 @@ teardown() { ! grep -q "^ssh " "$LOG" } -@test "a failed relay reports the desktop fallback and propagates failure" { +@test "every relay failing reports the desktop fallback and propagates failure" { cat > "$STUBS/ssh" <<'EOF' #!/bin/bash exit 255 @@ -70,6 +125,34 @@ EOF [[ "$output" == *"notify"* ]] } +@test "no reachable relay at all still fails rather than reporting success" { + # Every candidate is this machine, so the loop body never runs. rc must stay + # at its non-zero seed: a skipped loop is not a delivered message. + AGENT_TEXT_RELAYS="ratio.tailf3bb8c.ts.net" SELF_HOST=ratio HAS_ACCOUNT=0 \ + PATH="$STUBS:$PATH" run bash "$PAGE" nowhere to go + [ "$status" -ne 0 ] + [[ "$output" == *"notify"* ]] + ! grep -q "^ssh " "$LOG" +} + +@test "a local send failure blames the local send, not the relay list" { + # On the machine that holds the account, no relay is ever consulted. A + # message naming the relay list sends whoever is debugging it off chasing + # the tailnet while the fault is on the box in front of them. + cat > "$STUBS/signal-cli" <