cmux-host: bound the Cloud agent's event queue, identify read and activity line
What
Three bounds on the Cloud agent in cmux-host, from the crash audit (manaflow-ai/cmuxterm-hq#5351, items 2 and 3):
- Event queue:
EventQueueholds at most 256 events (EVENT_QUEUE_MAX; each is already capped at 4 KB, so about 1 MB). Past that,emitrefuses new events and the session logs "socket line refused: event queue full". The events it already holds keep their order, and the in-flight head is never dropped. Before, the queue grew without bound while it backed off (up to 600 s) and nothing drained it. query_identify: one deadline now covers the whole exchange, where before each read had its own timeout and a byte every 1.5 s beat the 2 s timeout forever. A reply line over 64 KB (MAX_LINE) is refused, even when its newline arrives in the read that crosses the cap. The write gets the time left.- Activity stream: an unfinished line over 64 KB counts as a dropped stream (it reconnects with backoff). Before, the buffer kept growing and was rescanned on every read. The check lives in
read_lines, which the agent socket's clients now share; they already closed on the same condition. On a read error, the complete lines already buffered are still handled, as the activity path did before.
Audit text: "role.rs:210-217 query_identify has no take() or overall deadline (a byte every 1.5 s beats the 2 s read timeout forever); role.rs:783-786 read_activity ignores read_available's over-MAX_LINE signal", and "Cloud agent event queue is unbounded ... Fix: cap the queue (256 events plus a byte budget), per-client idle deadline."
Not in this PR: the per-client idle deadline on the agent socket. Sixteen idle clients can still hold every slot, and a token asker gets "busy". UnixStream::connect in query_identify also stays unbounded, since std has no Unix connect timeout and the audit item was the read drip. All of these are same-user or group cmux only.
Part of manaflow-ai/cmuxterm-hq#5351 (items 2 and 3).
Tests
The tests went in first:
event_queue_tests::a_backed_off_queue_refuses_events_past_its_caprole_tests::identify_from_a_daemon_that_drips_bytes_ends_at_its_deadline(a byte every 100 ms against a 300 ms timeout)role_tests::identify_stops_reading_a_line_over_max_line(1 MB with no newline)role_tests::identify_refuses_a_whole_reply_over_max_line(added with the fix; it covers the boundary case review found)role_tests::a_line_over_max_line_ends_the_stream(added with the fix, sinceread_linesis new)
Results (Linux fast checks, cargo test -p cmux-host --lib -- cloud::):
- Red at the test-only commit 7013b6534 (run 41159399704): the three tests fail (
identify outlived its deadline: Timeout, the 1 MB line read pastMAX_LINE, the queue grew past 256). - Green at beee6350b (run 41160120623):
test result: ok. 27 passed; 0 failed.cargo clippy -p cmux-host --all-targets -- -D warningsprinted nothing.
Review: an independent Codex (gpt-6-astra, high) review blocked 84006ba0d. A line whose newline arrived in the read that crossed MAX_LINE got past the cap; the write had the full timeout; a read error dropped buffered lines; and a timing assertion was tight. beee6350b fixes all four. The delta review returned SHIP at beee6350b5c453d3250d430bca0a60ac5e7b88fe.
Risk
Low. A healthy daemon's identify reply is a few hundred bytes and arrives at once. A real event burst over 256 while the agent is backed off now loses the newest events, with a log line, instead of growing memory. No UI change.
Agent session: agent session
š¤ Generated with Claude Code