cmux link: the VM receiver logs why each stream ended
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::acceptdiscardedserve_host_inbound's result.- The policy-cancel branch said nothing.
acceptreturned 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,AddressMismatchorAuthority.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 as0 bytes back.
The line never carries the token or any byte of the stream.
How:
serve_host_inbound_tracedandhand_to_entry_tracedrecord what the stream came to in aStreamTrace.serve_host_inboundandhand_to_entrykeep 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-tuiandcargo 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
StreamTraceand 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](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
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, ...), plusStaleEpoch,AddressMismatch,Authority, andInboundrefusals. - Logs
closed by the receiverwhen 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_inboundandhand_to_entrykeep their signatures; the traced variants record into aStreamTraceand add 3 new tests.