Skip to content

Idle scanner SIGTERMs apps with a live WebSocket tunnel #61

Description

@nonreagent

Type: AFK — surfaced during review of #53.

Problem

touch_idle fires once per request in handle() (crates/adj/src/proxy.rs), before forward() runs. Once a request upgrades to a WebSocket, bytes flow through the detached copy_bidirectional tunnel and never touch last_request again. Vite/Webpack/Next HMR heartbeat pings ride inside that tunnel, so they don't count as activity either.

Net effect with the 15m idle_timeout default: a browser tab sitting on a dev server over HMR — no other HTTP traffic — looks idle to the scanner. After 15m the scanner SIGTERMs the app; the HMR client reconnects, lazy-boots it again, and the cycle repeats every 15m. Self-healing, but a restart loop is exactly the churn Adjacent exists to prevent.

(#53 correctly scoped this out; filing so it doesn't evaporate.)

Proposed fix

Make idle tracking tunnel-aware. Options, cheapest first:

  • Touch on tunnel close (floor): the tunnel task calls touch_idle when copy_bidirectional returns, so the idle clock restarts from when the session actually ended rather than when it began.
  • Periodic touch while the tunnel is open: a timer at a cadence well under idle_timeout (e.g. every 60s, or idle_timeout / 4) that stops when the tunnel closes. This is what actually prevents the mid-session kill.

Touch-on-close alone does not prevent the 15m mid-session SIGTERM — it only fixes the clock after the fact. A periodic touch is needed to keep a long-lived HMR session alive. Tradeoff: a naive interval keeps the app alive for any open tunnel, even one carrying no bytes; tying the touch to observed copy progress is more faithful but more code. Pick based on how much that matters in practice.

Acceptance criteria

  • A WS session with an open tunnel keeps its app alive past idle_timeout.
  • An app whose only WS tunnel has closed still idle-stops after idle_timeout.
  • Integration test: boot a WS echo app with a short idle_timeout, hold a tunnel open across that window, assert the app stays Running; then close the tunnel and assert it idle-stops.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions