cmux link: the VM receiver logs why each stream ended

Merged
TE
2d ago
+295-16

Summary

A Native daemon stream that a Cloud VM's receiver refused or closed ended with no byte and no log line on either side. A dial reached path_state: tunnel and the Mac's service hello went out with a fresh link token. Then the VM closed the daemon stream 3 to 9 s later without writing anything, and nothing said which check dropped it:

  • NativeReceiver::accept discarded serve_host_inbound's result.
  • The policy-cancel branch said nothing.
  • accept returned early with no word when the stream count was at its cap, or when the key was no policy peer.

Now each stream ends with one stderr line, which goes to the host journal (journalctl -u cmux-host). The pr-capture cloud fixture already collects that journal from its own machine before deleting it. The line gives the stream id, the service, the overlay source address and the time, then one of:

  • refused: token <TokenRefused>: the verifier's own reason (Invalid, Expired, Replayed, Missing, ...).
  • refused: StaleEpoch, AddressMismatch or Authority.
  • refused: Inbound(EntryUnavailable | NotAnEntry | BadHello).
  • closed by the receiver: its peer left the policy, or the receiver stopped.
  • spliced: N bytes in, M bytes back: the stamp reached the daemon entry. A daemon entry that closes the stream itself reads as 0 bytes back.

The line never carries the token or any byte of the stream.

How:

  • serve_host_inbound_traced and hand_to_entry_traced record what the stream came to in a StreamTrace.
  • serve_host_inbound and hand_to_entry keep their signatures, so the existing callers and tests are unchanged.

Testing

cmux-ci test (fleet Linux), at the tree of 265e43ed5ee0:

  • cargo test -p cmux-tui --lib link::host_inbound: 24 passed, including the 3 new tests (a_refused_stream_says_which_check_refused_it, a_spliced_stream_reports_its_bytes_each_way, a_stream_the_receiver_closed_says_so).
  • cargo test -p cmux-tui --lib link::native_receiver: 6 passed.
  • cargo fmt --check -p cmux-tui and cargo clippy -p cmux-tui --lib --tests: clean.

Observed / Not run

  • Observed: the tests above.
  • Not run: the line in a live VM journal. That comes from the next dev capture once an image carries this commit.
  • The new tests could not fail on the base, because StreamTrace and the line did not exist. Before this change the VM said nothing at all.

Changelog

none

Agent session: agent session

🤖 Generated with Claude Code


Autofix with [code]smith
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Makes the Cloud VM receiver log one line per stream explaining how it ended, so refused or closed daemon streams are attributable instead of closing with no word on either side. The line goes to stderr and so to the host journal (journalctl -u cmux-host), and never contains the token or any stream byte.

  • Logs the token verifier's own reason (Invalid, Expired, Replayed, Missing, ...), plus StaleEpoch, AddressMismatch, Authority, and Inbound refusals.
  • Logs closed by the receiver when its peer left the policy or the receiver stopped, and byte counts each way once a stream spliced.
  • The receiver also logs why it drops a stream before serving it when the stream cap is reached or the key is no longer a policy peer.
  • serve_host_inbound and hand_to_entry keep their signatures; the traced variants record into a StreamTrace and add 3 new tests.