Skip to content

feat(ws-client): record when the last frame of any kind arrived - #8480

Closed
holmes wants to merge 1 commit into
block:mainfrom
holmes:holmes/ws-last-frame-at
Closed

holmes wants to merge 1 commit into
block:mainfrom
holmes:holmes/ws-last-frame-at

Conversation

@holmes

@holmes holmes commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Summary

NostrWsConnection answers the relay's WebSocket pings inside its receive loops and never returns them. So a client that detects stalled sockets by watching for inbound frames cannot see the relay's heartbeat. The relay pings every connection every 30 s and closes it after three missed pongs (heartbeat_loop in crates/buzz-relay/src/connection.rs). Desktop's relayStallWatchdog counts any inbound frame, the relay's pings included, as proof the socket is alive. A Rust client on this crate cannot do the same: to it, an idle, healthy socket looks silent, and a stall watchdog tears it down. ManagerBot's mobile core runs a 60 s stall watchdog, and it reconnected idle sockets about every 72 s for this reason.

This PR adds NostrWsConnection::last_frame_at() -> tokio::time::Instant: when the last frame of any kind (Text, Ping, Pong or Close) arrived from the relay. All three receive loops record it: recv_one, wait_for_auth_challenge and wait_for_ok. Until the first frame arrives, it holds the connect time. The change is additive; no existing signature or behaviour changes.

buzz-ws-client's tests were in neither unit lane, so this also adds the crate to just test-unit and to the scripts/run-tests.sh fallback.

Related issue

None found.

Testing

  • New a_ping_alone_advances_last_frame_at: a loopback relay sends one Ping and then stays silent. next_event times out with nothing returned, the relay receives the Pong, and last_frame_at() has advanced. With the stamp in recv_one removed, the test fails.
  • cargo test -p buzz-ws-client --lib
  • cargo clippy -p buzz-ws-client -p buzz-cli -p buzz-test-client --all-targets -- -D warnings
  • cargo fmt --all -- --check, just file-size-check, and the pre-push hooks, including just test-unit

🤖 Generated with Claude Code

NostrWsConnection answers the relay's pings inside its receive loops and
never returns them, so a caller has no way to see that an otherwise idle
socket is still alive. The relay pings every connection every 30 s; a
client stall watchdog that counts inbound frames, as desktop's
relayStallWatchdog does, sees an idle healthy socket as silent and tears
it down.

Every receive loop (recv_one, wait_for_auth_challenge, wait_for_ok) now
stamps the arrival of each frame, Ping, Pong and Close included, and
last_frame_at() returns it: the connect time until the first frame.

The crate's tests join the unit lane, since nothing ran them before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jason Holmes <holmes@squareup.com>
@github-actions

Copy link
Copy Markdown

🔐 Codex Security Review

Status: review required for the current range.

The current range is 6e6173acd58029bac800583c4cc7482dafed693a...d97dda4de43c0103c6a758cc474d91975065dea6.
A new review must complete for this exact range. When manual authorization
is required, a user with write access must comment exactly
@buzz-security-review d97dda4de43c0103c6a758cc474d91975065dea6 to authorize a new review.
Any previous review applies only to its recorded range.

@holmes
holmes marked this pull request as ready for review October 20, 2026 00:37
@holmes
holmes requested a review from a team as a code owner October 20, 2026 00:37
@holmes holmes closed this Oct 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant