Skip to content

proxy-relay: accepted client connections have no timeout — an idle or partial-line client is held forever (only the upstream CONNECT is timed) #2234

Closed

Description

@dezren39

Summary

bin/proxy-relay.mjs never times out an accepted client connection. The only socket timeout in the file guards the outbound tunnel CONNECT (10s, proxy-relay.mjs:93, cleared at :116); the accepted client socket gets none — not while the relay waits for the first request line, and not after the connection is piped upstream.

Evidence (main 24901e7b)

  • bin/proxy-relay.mjs:155-184 — the net.createServer handler buffers the first line in onData (headEnd === -1 && buf.length < 8192 → keep waiting). No client.setTimeout(...) is ever called on the accepted socket; grep setTimeout bin/proxy-relay.mjs returns only lines 93 and 116, both on the upstream socket.
  • Probe (this run, sandbox, relay on loopback :18999, target 18745): opened (1) a TCP connection that sent nothing, and (2) a connection that sent a partial request line GET /hea with no CRLF. After 12s — longer than the upstream CONNECT timeout — both sockets were still open (idleClosed=false partialClosed=false, neither destroyed). The partial-line socket was still holding its buffered bytes in the peek stage. Nothing in the code ever reaps either state, so they live until the client or the OS gives up.
  • After the peek stage the same gap continues: client.pipe(upstream); upstream.pipe(client) (:178-179) with no idle timeout on either side, and the upstream's CONNECT timeout is explicitly cleared (sock.setTimeout(0), :116) once the tunnel is up.

Why it matters

The relay is a long-lived loopback forwarder supervised for fleet proxies. Any local client that connects and stalls — a hung OpenCode/proxy client, a crashed process that left a half-open socket, a health prober that connects but never writes — pins a file descriptor and its buffered bytes in the relay forever. Enough of them accumulate into fd exhaustion on a process whose whole job is to stay up, and the failure mode is silent: nothing logs, nothing expires. Every other timeout-sensitive surface in this fleet has been pinned down one by one (upstream CONNECT here included); the accepted-socket side is the unguarded one.

Acceptance criteria

  • An accepted client connection that does not deliver a complete first line within a bounded time is closed by the relay (triage pins the value; the upstream CONNECT timeout of 10s is the in-file precedent).
  • The timeout behaviour after piping (idle forwarded connections) is decided explicitly — either an idle timeout is applied, or the code comments why forwarded connections are intentionally exempt.
  • A test (alongside bin/proxy-relay coverage or a scripted probe) shows an idle connection and a partial-line connection are both reaped within the bound, while a normal /health answer and a forwarded request still work.
  • No change to target selection, /health local answering, or endpoint publishing behaviour.

Found by new-issue-creator-code-b (lane B) this run; probe in the goal's hidden_files probe-2026-10-29-relayidle/ (probe.mjs + relay.log), main 24901e7b.

Activity

  1. developing-today commented on Oct 29, 2026

    @developing-today
    Contributor
  2. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    priority/lowNice to have; do when convenient
    queue/queuedHas a queued task in the Supabase agent queue
    source/creatorFiled by the new-issue-creator job
    task/triageQueue packet kind: verify, reproduce, and route this item
    app/proxy-relaybin/proxy-relay.mjs sandbox-to-laptop relay
    effort/sSmall: expected under 2 hours of work
    on Oct 29, 2026
  3. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

    • Agent: new-issue-creator (new-issue-creator-code-b)
    • Session / ID: new-issue-creator-code-b + run time 2026-10-29T10:20:00Z
    • Found via: filed this run
    • Task: Triage the proxy-relay accepted-socket timeout gap: pin the first-line deadline (upstream CONNECT's 10s is the in-file precedent) and decide the post-pipe idle policy, then route the fix.
    • Expectation: effort/s — done when a triage packet pins the timeout value(s) and the test approach in the issue's acceptance criteria.
    • Links: probe probe-2026-10-29-relayidle/ in the goal's hidden_files (idle + partial-line sockets still open at 12s, main 24901e7)
  4. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 1, 2026
  5. 2 remaining items

  6. added
    queue/queuedHas a queued task in the Supabase agent queue
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/claimedQueue worker lifecycle state
    work/assignedQueue worker lifecycle state
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 1, 2026
  7. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    agent-note:v1 — state: picked-up at=2026-11-02T02:45:51Z (OpenChamber scheduled lane)

  8. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    agent-note:v1 — state: done at=2026-11-02T02:55:14Z (OpenChamber scheduled lane)

  9. added a commit that references this issue on Nov 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/proxy-relaybin/proxy-relay.mjs sandbox-to-laptop relayeffort/sSmall: expected under 2 hours of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/lowNice to have; do when convenientsource/creatorFiled by the new-issue-creator jobtask/triageQueue packet kind: verify, reproduce, and route this item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions