Skip to content

issues: servers let a client go on the handshake limit with its request unread, and one program that fills a server's table holds every other client out; netstack_socket_churn is cut - #897

Merged
Japabu merged 3 commits into
mainfrom
wt/toyos-netstack-timeout-issue
Oct 12, 2026

Conversation

@Japabu

@Japabu Japabu commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator

Files the server doctrine's record of a pass that lets a connection go on its handshake limit before reading the request already whole in it (issues/servers-let-a-client-go-on-the-handshake-limit-with-its-request-unread.md). Files the isolation hole that a server's table is one number for every client (issues/one-program-that-fills-a-servers-table-holds-every-other-client-out.md). Cuts netstack_socket_churn, an integration test of a userland program, under reviewer.md's Tests. No production code changes. Net lines: +187 −251 (git diff --shortstat origin/main...HEAD). Production: 0. Tests: −251 (the guest binary −209, the harness −40, the netcase image's comment −2). Issues: +187.

Why

While #886 was being built, a whole cargo test at its then-head be929d941 went red on netstack_socket_churn (summary, guest EXIT=1). netstack logged dropping client 86031 — it never finished its request, and the test's connect loop then read NetstackNotFound where it asserts ResourceExhausted (guest.log L647-L682). The same gates at f126fb514 were green (summary). #886 touches nothing under system/netstack, sdk/toyos/src/net.rs or the test (git diff --quiet ecd6d1cc5 be929d941 -- … exits 0, untouched.txt).

What the record says, and why

Its lines are pinned at 1a7464692, this branch's base.

  • Scope: the server doctrine. Eight servers declare a private HANDSHAKE_TIMEOUT of 2 s (searches). Six let a connection go before reading what the same wait reported: netstack, the compositor, diskserver, filepicker, logkeeper and usbd. The supervisor and fileserver serve first, as 5a68db78b made init do after a stop asked at 2.196 s was dropped unread at 4.753 s. That commit is a recorded real failure of the same order, so it is the independent oracle. soundserver's 63 control slots and every surface host's 4 (console, terminal) are in it too, per the owner's ruling: they hold a silent connection with no limit, and accept and close one past a full table.
  • The limit stays; the order is the defect. The exit keeps the limit on a connection that has sent no whole request, with its drop logged by name. It requires each of the eight to read a connection before it lets it go on the limit, and soundserver and every surface host to read a connection before they let it go.
  • Its length. RFC 9110 §15.5.9 gives an HTTP server a limit on a request not received whole, and RFC 9112 §9.5 sets no length. The published defaults, cited at pinned commits, are nginx's client_header_timeout of 60 s and Apache httpd's header=20-40,MinRate=500. 2 s is a tenth of the shorter. The exit takes the length from them, or has it defended against them at the declaration.
  • One declaration beside toyos::ipc::FrameRx that takes its clock as an argument, with a host test that goes red when a connection whose request is whole is let go on a late pass.
  • A full table: block, everywhere. soundserver and every surface host are included. RFC 9112 §9.5 asks a server to let flow control resolve a temporary overload, and past the port's 32 the kernel refuses the connect ResourceExhausted by name (kernel/src/object/port.rs).
  • The supervisor's power port gets a table of its own (ruling 2). Its launcher, grants, swap and power connections share one table today (main.rs:1213-1248). Under blocking, a stop would then wait behind silent launcher connections, the lateness 5a68db78b recorded.
  • What the let-go client reads (ruling 3). Following nginx, which closes on the limit with no answer (ngx_http_request.c L1130-L1134), the server closes with nothing said. The client names that as the server letting it go, never NetstackNotFound, and every client library of these servers does the same.
  • Logged by name, logkeeper included. A drop on the limit comes at the server's rate, at most its table's size per limit, so it is no channel for a client to write into the log.
  • The wait. No server waits a fresh whole limit. Each wakes by what is left of its oldest entry's.
  • Owner: the orchestrator. Nobody holds issues/toyos-has-its-own-network-stack.md.
  • No Linux. The record cites the RFCs, nginx's and Apache httpd's documentation and source, and the tree. git grep -i -e linux -e d-bus -e dbus -e systemd on both files exits 1 (searches).

The isolation hole

Filed in this branch under ruling 4. A server's table of connections it has not served is one number for every client. One program that reconnects each time it is let go keeps the eight limited tables full for as long as it runs. It holds soundserver's or a surface's slots, which have no limit, until it hangs up. Once full tables block and the limit is 20-60 s, every other client waits behind it. Only fileserver shares its table out, per session. The record names which connections carry a badge that could key a share, and points at the two existing netstack records of the same hole on its places and lookups. Its exit: each table holds at most a per-client share, keyed by a badge that whoever mints the connector writes, the next connection past it refused by name, with a host test of each table. The supervisor writes it for the servers it spawns, whose ports it creates (system/supervisor/src/main.rs:410, :424). Console and terminal write it for their own ports, which each creates and moves only into the shell it spawns (system/console/src/main.rs:154-156, system/terminal/src/main.rs:55-56). Owner: the orchestrator.

The cut

netstack_socket_churn starts netstack in a guest and drives it through a sequence of steps. #890's Tests rule refuses that outside the kernel, and the owner's ruling makes its deletion that cut, not a test owed back. c6a2861e8 deletes the guest binary, its RUST_SKIP and MACHINE_TESTS entries, its harness function and dispatch arm, and the netcase.toml comment that named it as a reason test-runner receives netstack. netstack_streams, libc_sockets and https_fetch still need that grant. git grep -n socket_churn HEAD -- ':!issues' exits 1.

What it reached of the node is held by these unit tests, which run in the host gate (searches):

  • system/netstack/node/tests/listeners.rs: a_connect_past_the_places_is_refused_and_sends_nothing, a_departed_clients_connection_holds_its_place_until_it_is_cut, a_wake_the_owners_pipe_refuses_ends_the_listener;
  • system/netstack/node/tests/streams.rs: a_client_that_left_is_finished_by_the_stack, a_writer_that_leaves_after_the_shutdown_leaves_the_stream_to_its_reader, a_pipe_that_is_no_pipe_resets_its_connection, a_refused_watch_resets_only_a_stream_that_holds_the_end.

What only system/netstack/src/serve.rs does has no test since the cut, and that file carries no #[test] (git grep -c exits 1). The record's exit names these as owed:

  • a second receive refused ERR_RESOURCE_EXHAUSTED (serve.rs:563-567);
  • a waiting receive answered by its datagram (settle, :676-680);
  • the kernel's watch answers that end a stream, a listener or a datagram socket (Sockets::answered, :798-843).

Round 3's findings

finding answer
BLOCKER: the isolation record keys every share by a badge the supervisor mints, which console's and terminal's ports never see The exit keys each share by a badge that whoever mints the connector writes: the supervisor for the servers it spawns, console and terminal for their own ports. The record states that console and terminal create their own ports.
NOTE: this body and the commit's message say the same Both changed with it. The commit is f81eea55c.

Round 2's findings

finding answer
BLOCKER: the exit's last bullet and :94's restore Both deleted. The record's "The cut test" names the seven node tests, and the exit names the three serve.rs tests as owed.
BLOCKER: the body justifies the cut as a red-test deletion with a restore "The cut" above: Tests' cut, with the holding tests named.
NOTE: title, unsure bullet, 9f926c672's message The branch is rebuilt on origin/main (1a7464692), and the cut is c6a2861e8, whose message states the cut under Tests. The title says the cut. The unsure bullet is gone, because the ruling decided it.
NOTE: soundserver and surface::Host::accept In the record and its exit (ruling 1).
NOTE: the isolation hole Filed: issues/one-program-that-fills-a-servers-table-holds-every-other-client-out.md (ruling 4).
NOTE: the supervisor's shared table Stated, and the exit gives the power port its own table (ruling 2).
NOTE: what the let-go client reads The exit follows nginx (ruling 3).

Gates

gate head exit log
cargo run -- --ci host f81eea55c 0 (Host: 76 step(s), all green, L8829) host.log, host.exit, host.head

The host's one-minute load averages were 33.73 as the gate began and 28.13 as it ended (host.load). The FAILED lines in the log come from the gate's negative-control steps, which inject defects on purpose. The gate's clippy --all-targets step compiles tests/toyos.rs with the cut function gone. Its toyos-checks step checks the guest binaries under tests/guest/rust-tests/src/bin against RUST_SKIP (tests/checks.rs). The diff reaches no other guest test: it changes no other test's code and no image beyond a comment.

What I am unsure of

  • The mechanism is read from the code, not reproduced. No run held a request back to show it dropped, and the race at the supervisor and fileserver has never been seen.
  • Whether client 86031 or netstack was late is not known, and the red run's host load was not recorded.
  • The isolation record's key for a per-client share is a badge. Today only netstack's and the supervisor's launcher and grants connections carry one that names a program. The exit says who writes the others' badges, not what they name.

🤖 Generated with Claude Code

https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy

@Japabu

Japabu commented Oct 12, 2026

Copy link
Copy Markdown
Collaborator Author

Review, round 1, at b12c6e91d, against .claude/agents/reviewer.md on origin/main (97f07c381).

Net lines: +52 −0, all of it the one new file in issues/. Production 0, tests 0. The host gate at this head: host.head reads b12c6e91d65244e027507ac6c7b9e38883328c2a, host.exit reads EXIT=0, and host.log L10199 reads Host: 76 step(s), all green. The diff reaches no guest test.

BLOCKER

  • issues/netstack-drops-a-client-whose-request-it-has-not-read-two-seconds-after-accepting-it.md:46-50 — the exit forbids any time limit on a client that has connected and not yet sent its request ("never on elapsed time … nothing else times a client in their place"). Published practice keeps exactly that limit. — D-Bus 1.8.8 cut the system bus's auth_timeout from 30 s to 5 s as its fix for CVE-2014-3639, "avoiding denial of service by using up all unauthenticated connection slots". 1.8.12 put it back to 30 s because 5 s "has been reported to cause boot regressions … with parallel boot (systemd) on slower hardware". nginx's client_header_timeout defaults to 60 s. Apache's default is RequestReadTimeout header=20-40,MinRate=500. The exit leaves that line and cites none of it, which is Fit's BLOCKER. Lines 21-24 of the file say this limit is the only thing that drains MAX_PENDING_CONNS. So once the exit is met, one program that holds 32 silent connections refuses netstack to every other program for as long as it lives. That is an isolation hole, and the exit requires it. It also forbids what root CLAUDE.md's "bounded by a timeout that fails loudly" asks for. The limit itself is not the defect.
  • issues/netstack-drops-a-client-whose-request-it-has-not-read-two-seconds-after-accepting-it.md:9-19 — the defect this file shows is the order of the loop: system/netstack/src/main.rs:449-456 lets connections go before the read loop at :498 has read the ones the same wait reported ready. The tree has already recorded this exact defect and fixed it without removing the limit. — Commit 5a68db78b ("init serves a request before it expires a client") records a real failure: init expired clients before serving the requests that had arrived, so quiesce_writers' stop, sent at 2.196 s, was dropped at 4.753 s. That commit put the serve first. system/supervisor/src/main.rs:1276-1286 still reads "After the requests that arrived are served: a client whose request is here is answered however late this loop came round to it", and system/fileserver/src/main.rs:411-428 also reads first and expires after. The file names neither that fix nor that record, and its exit gives netstack a second policy beside the one the tree already has. It is also the independent oracle that the body's "What I am unsure of" says is missing. The exit should require three things. First, every request whole in its connection is served before any pending connection is let go on the limit. Second, the limit stays, on a connection that has sent no whole request, and its drop is logged by name; its length is set from the practice above and cites it, or is defended against it. Today's 2 s is 15x shorter than D-Bus's 30 s, and the log cannot say whether the client was the late side (:43-44). Third, a test that can fail: a request whole in its pipe when a pass arrives past the limit is answered.
  • issues/netstack-drops-a-client-whose-request-it-has-not-read-two-seconds-after-accepting-it.md (scope) — this belongs to the server doctrine, not to netstack alone. — The same expiry-before-read order is in six servers: netstack main.rs:449-456, compositor session.rs:361-369 (ahead of take_frames at :388), diskserver main.rs:687-688, filepicker main.rs:537-544, logkeeper inspect.rs:153-154 and usbd main.rs:136-139. All eight servers carry a private HANDSHAKE_TIMEOUT = 2 s. system/CLAUDE.md calls "a server never blocks on a client" "the doctrine no single site owns" and names ipc::FrameRx as the mechanism these servers share. An exit that covers netstack alone leaves five siblings that can still drop a whole request, and lets the fix land as a netstack copy. Root CLAUDE.md's "Stay on the task" files what is found off the fence. So: one record whose exit names every site, or one declaration they all read. If that declaration sits beside FrameRx in toyos::ipc and takes its clock as an argument, the test above runs on the host tier.
  • issues/netstack-drops-a-client-whose-request-it-has-not-read-two-seconds-after-accepting-it.md:52 — the owner is "whoever holds issues/toyos-has-its-own-network-stack.md", and that track's status: open means, in issues/README.md, "nobody is holding it". — An issue written without an owner that exists is a BLOCKER. Moved to the doctrine, the record has an owner who exists, the orchestrator, as in issues/guest-tests-a-loaded-host-can-turn-red.md.

NOTE

  • issues/netstack-drops-a-client-whose-request-it-has-not-read-two-seconds-after-accepting-it.md:16-17 — "The client it drops is live and waiting for its answer" is true only if netstack was the late side, and :43-44 says the log cannot tell which side was late.
  • issues/netstack-drops-a-client-whose-request-it-has-not-read-two-seconds-after-accepting-it.md:22-23 — "refuses a new client by name": only the log names it. — system/netstack/src/main.rs:482-488 accepts the connection and drops it, so the client reads NetstackNotFound through hangup, the same symptom as this defect. Filepicker, supervisor, usbd and logkeeper do the same. diskserver (main.rs:650) and fileserver (main.rs:390) stop watching the acceptor once full, which is D-Bus 1.8.8's "make new connection attempts block instead of disconnecting them". The re-scoped exit should say which of the two every server does.
  • system/netstack/src/main.rs:435-437, system/logkeeper/src/inspect.rs:144-145 — each pass sleeps a fresh full HANDSHAKE_TIMEOUT rather than what is left of the oldest connection's limit. system/supervisor/src/main.rs:1173-1178 calls that "a bound that is not the bound", and the other servers compute what is left. This is one more divergence among the eight copies the re-scoped record covers.
  • tests/guest/rust-tests/src/bin/netstack_socket_churn.rs:134 — the test stays in the suite while the defect is open. Root CLAUDE.md has a red test fixed, or deleted with its issue recording the commit that restores it. This branch does neither, and the file does not say which, so the next unrelated branch can go red on it as The kernel loads pinned Intel and AMD microcode on every CPU before anything reads what an update changes, and a CPU that refuses one says so #886 did. The orchestrator decides which.
  • PR body, "Found off the fence, not filed" — "the same 2 s HANDSHAKE_TIMEOUT … also lets a client go in seven other servers" is true of the limit. The drop of a request already whole happens in five of those seven: supervisor and fileserver serve first.

SEND BACK

@Japabu Japabu changed the title issues: netstack drops a client whose request it has not read two seconds after accepting it issues: servers let a client go on the handshake limit with its request unread, and netstack_socket_churn leaves the suite until it is fixed Oct 12, 2026
@Japabu

Japabu commented Oct 12, 2026

Copy link
Copy Markdown
Collaborator Author

Ruling on the conflict with #890's Tests rule. netstack_socket_churn is an integration test of a userland program, and #890's Tests rule cuts it. So its deletion in 9f926c672 is a cut, not a test owed back. The record's exit drops "back in the suite, green". In its place, the host test beside toyos::ipc::FrameRx holds the order, and netstack's own unit tests hold what the churn reached that the order does not. The record names them.

@Japabu

Japabu commented Oct 12, 2026

Copy link
Copy Markdown
Collaborator Author

Review, round 2, at 8679b225e, against .claude/agents/reviewer.md on origin/main (524ee0859).

Net lines. +114 −251 (git diff --shortstat origin/main...8679b225e, 4 files): production 0; tests −251 (the guest binary −209, the harness −40, netcase.toml −2); issues +114.

Host gate at this head. host.head reads 8679b225e15f0016f580a1ddf507d05c569a8ca0, host.exit reads EXIT=0, and host.log L8815 reads Host: 76 step(s), all green. The diff reaches no other guest test.

Sites at origin/main (524ee0859). Every order the record names still holds. Since 97f07c381, where the record pins its lines, three of the cited files changed:

  • the compositor's drop is now at session.rs:353-360, still ahead of take_frames at :379;
  • the supervisor serves and then lets go at main.rs:1253-1311;
  • netstack's main.rs changed only at :544.

The other cited files are byte-identical.

The length's sources. Each resolves at its pin:

  • nginx ngx_http_core_module.c L3620-3621 merges client_header_timeout to 60000;
  • httpd mod_reqtimeout.c L35-37 defines header 20, 40 and 500;
  • RFC 9110 §15.5.9 and RFC 9112 §9.5 say what the record says.

grep -i 'linux\|d-bus\|dbus\|systemd' on the file exits 1.

Round 1

  • BLOCKER, the exit forbids the limit: CLOSED. :102-103 keeps the limit on a connection with no whole request. :107-108 takes its length from the two defaults verified above.
  • BLOCKER, the defect is the order: CLOSED. The record states it at :18-30 and :100-101, with the host test at :104-106. 5a68db78b is on main (git merge-base --is-ancestor exits 0), and its message records the stop asked at 2.196 s and dropped at 4.753 s.
  • BLOCKER, the scope is the doctrine: CLOSED. git grep HANDSHAKE_TIMEOUT origin/main -- system finds the eight declarations that :11-14 names, and :104 makes them one.
  • BLOCKER, an owner that exists: CLOSED. :96 names the orchestrator, with status: open, as issues/guest-tests-a-loaded-host-can-turn-red.md:38 does.
  • The NOTEs are answered: "live and waiting" (:43-45), refusal by name (:53-62, :110-113), the fresh wait (:47-51, :109) and the body's "seven". The NOTE on the test was answered by 9f926c672, and the ruling has since recast it (below).

BLOCKER

  • issues/servers-let-a-client-go-on-the-handshake-limit-with-its-request-unread.md:114, :94 — the exit still ends "netstack_socket_churn is back in the suite, green", and :94 still says "git revert 9f926c672 restores it". — The owner's ruling (issues: servers let a client go on the handshake limit with its request unread, and one program that fills a server's table holds every other client out; netstack_socket_churn is cut #897 (comment)) makes the deletion Integration between modules is tested only for the kernel, and an integration test outside it is cut #890's Tests cut, not a test owed back, and has the record name the tests that hold what it reached. The changes:
    • Delete :114, and the restore sentence at :94.
    • Beside the FrameRx host test at :104-106, the exit names netstack's own tests. The node's half is held today, among others, by system/netstack/node/tests/listeners.rs's a_connect_past_the_places_is_refused_and_sends_nothing, a_departed_clients_connection_holds_its_place_until_it_is_cut and a_wake_the_owners_pipe_refuses_ends_the_listener, and by system/netstack/node/tests/streams.rs's a_client_that_left_is_finished_by_the_stack, a_writer_that_leaves_after_the_shutdown_leaves_the_stream_to_its_reader, a_pipe_that_is_no_pipe_resets_its_connection and a_refused_watch_resets_only_a_stream_that_holds_the_end.
    • The exit names as owed the tests for what only system/netstack/src/serve.rs does. That file carries no #[test], and no test has reached these since the cut, though the binary's unit tests run in the host gate (host.log L8472):
      • a second receive refused ERR_RESOURCE_EXHAUSTED (serve.rs:563-567);
      • a waiting receive answered by its datagram (settle, :676-680);
      • the kernel's watch answers that end a stream, a listener or a datagram socket whose owner left or whose end is no pipe (Sockets::answered, :798-843).
  • PR body, "The test's deletion" — it justifies the cut "as root CLAUDE.md deletes a red or flaky test", with a restore, and names no test that holds what was cut. — Under the ruling the cut is Tests'. That rule cuts only where "a unit or module test of its program holds it, named in the pull request body", and Guest tests makes any other cut a BLOCKER. The body names the tests of the finding above.

NOTE

  • PR title ("until it is fixed"), the body's first "What I am unsure of" bullet, and 9f926c672's message ("git revert of this commit brings it back, and the fix the issue records does") — false under the ruling, and they become main's record.

  • issues/…:110-113 — "none accepts a connection to close it because the table is full" reaches past the eight. soundserver (system/soundserver/src/control.rs:185-187) and toyos::surface::Host::accept (sdk/toyos/src/surface.rs:186-193, used by console and terminal) both accept a connection and close it when full. — The exit says whether they are in it.

  • PR body, "Found off the fence, not filed" — one program that refills a server's table holds every other client out for as long as it runs. The same is true of soundserver's 63 control slots and a surface's 4, which have no limit at all. No file in issues/ names it. Under :107-108 and :110-113, each other client waits a whole 20-60 s limit in the port's queue behind it, not 2 s. — This is an isolation hole, and root CLAUDE.md files what is found off the fence; the orchestrator, who set the fence, files it.

  • issues/…:110-113 — the supervisor keeps one table for its launcher, grants, swap and power connections (main.rs:1189-1223 at 97f07c381). Blocking on a full table therefore makes a stop through the power port wait a whole limit behind silent launcher connections, the lateness 5a68db78b recorded. — The exit says whether the power port shares that table.

  • issues/…:102-103 with :34-36 — the exit still leaves a client let go on the limit reading NetstackNotFound. The record's sources do not agree on whether to tell that client anything:

    • nginx closes with no response (ngx_http_request.c:1130-1134, ngx_http_close_request);
    • httpd closes with none when the request line never came (server/protocol.c:1455-1459), and answers 408 through ap_die on a partial header (:1114-1115, :1479-1483).

    — :34-36 lists that word as part of the defect, so the exit says which source it follows.

SEND BACK

@Japabu

Japabu commented Oct 12, 2026

Copy link
Copy Markdown
Collaborator Author

Rulings on round 2's NOTEs (#897 (comment)).

  1. Scope. soundserver's control slots and toyos::surface::Host::accept (console, terminal) are servers under the same doctrine. The exit covers them: they block when full like the eight, and read before they let go.
  2. The supervisor's power port gets a table of its own, so a stop never waits behind silent launcher connections.
  3. What the let-go client reads. Following nginx, the server closes with no answer. The client names it as the server letting it go, never NetstackNotFound, and each client library does the same.
  4. The isolation hole is filed in this branch: one program that fills a server's table holds every other client out. Its exit is a per-client share of each table.
  5. The title, the body and 9f926c672's account of the cut become true under the ruling. That commit is rewritten, since it is unpushed to main.

Japabu and others added 2 commits October 12, 2026 22:29
…rogram

netstack_socket_churn starts netstack in a guest and drives it through a
sequence of steps against the harness's host servers, which reviewer.md's
Tests rule (#890) refuses outside the kernel: it is cut, and what it
checked is held by netstack's own tests or recorded as owed.

What it reached of the node is held by system/netstack/node/tests:
listeners.rs's a_connect_past_the_places_is_refused_and_sends_nothing,
a_departed_clients_connection_holds_its_place_until_it_is_cut and
a_wake_the_owners_pipe_refuses_ends_the_listener, and streams.rs's
a_client_that_left_is_finished_by_the_stack,
a_writer_that_leaves_after_the_shutdown_leaves_the_stream_to_its_reader,
a_pipe_that_is_no_pipe_resets_its_connection and
a_refused_watch_resets_only_a_stream_that_holds_the_end.

What only system/netstack/src/serve.rs does has no test since this cut:
a second receive refused ERR_RESOURCE_EXHAUSTED, a waiting receive
answered by its datagram (settle), and the kernel's watch answers that end
a stream, a listener or a datagram socket (Sockets::answered).
issues/servers-let-a-client-go-on-the-handshake-limit-with-its-request-unread.md
records them as owed.

It was also red once, in a whole cargo test at be929d9: netstack let
client 86031 go on its handshake limit, and the connect loop read
NetstackNotFound where it asserts ResourceExhausted. That defect is the
record above.

The cut takes the guest binary, its RUST_SKIP and MACHINE_TESTS entries,
its harness function and dispatch arm, and the netcase image's comment
that named it as a reason test-runner receives netstack; netstack_streams,
libc_sockets and https_fetch still need that grant.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
…st unread

Eight servers carry a private two-second HANDSHAKE_TIMEOUT, and six of
them let a connection go on it before reading what the same wait
reported, so a request already whole is dropped when the pass that would
read it comes late. The supervisor and fileserver serve first, as
5a68db7 made init do after a stop asked at 2.196 s was dropped unread
at 4.753 s; that commit is the recorded real failure of the same order.
netstack_socket_churn went red that way once, at be929d9.

The record covers soundserver's control slots and the surface hosts,
servers under the same doctrine, which hold a silent connection with no
limit and accept and close one past a full table. Its exit, under the
owner's rulings on PR #897: every server reads before it lets go; the
limit stays, logged by name, as one declaration beside toyos::ipc::FrameRx
that takes its clock, with a host test of the order, and a length taken
from nginx's and Apache httpd's published defaults; no fresh whole-limit
wait; a full table blocks, as RFC 9112 section 9.5 asks; the supervisor's
power port gets a table of its own; a connection let go is closed with no
answer, as nginx does, and its client names it as the server letting it
go, never NetstackNotFound; and system/netstack/src/serve.rs gets tests
for what the cut churn test alone reached. The tests that hold the rest
of what it reached are named.

The owner is the orchestrator: nobody holds the network track.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
Japabu added a commit that referenced this pull request Oct 12, 2026
…ent out

Found while recording the handshake limit's order, and filed under the
owner's ruling on PR #897. A server's table of connections it has not
yet served is one number for every client: one program that reconnects
each time it is let go keeps the eight limited tables full for as long
as it runs, and holds soundserver's 63 control slots or a surface's 4,
which have no limit, until it hangs up. Once full tables block and the
limit takes a length of 20 s to 60 s, every other client waits behind it.

Only fileserver shares its table out, per session's share. The exit is a
per-client share of each table, keyed by a badge the supervisor mints,
refused by name past it, with a host test of each table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
@Japabu
Japabu force-pushed the wt/toyos-netstack-timeout-issue branch from 8679b22 to ff3b669 Compare October 12, 2026 20:36
@Japabu Japabu changed the title issues: servers let a client go on the handshake limit with its request unread, and netstack_socket_churn leaves the suite until it is fixed Servers read a connection before they let it go on the handshake limit, and one program cannot hold a server's table: both filed; netstack_socket_churn is cut Oct 12, 2026
@Japabu Japabu changed the title Servers read a connection before they let it go on the handshake limit, and one program cannot hold a server's table: both filed; netstack_socket_churn is cut issues: servers let a client go on the handshake limit with its request unread, and one program that fills a server's table holds every other client out; netstack_socket_churn is cut Oct 12, 2026
@Japabu

Japabu commented Oct 12, 2026

Copy link
Copy Markdown
Collaborator Author

Review, round 3, at ff3b669ad, against .claude/agents/reviewer.md on origin/main (1a7464692, the branch's merge base).

Net lines. +183 −251 (git diff --shortstat origin/main...ff3b669ad, 5 files): production 0; tests −251 (the guest binary −209, tests/toyos.rs −40, netcase.toml −2); issues +183.

Host gate at this head. host.head reads ff3b669adc70184b3678c5ed7916bfa3f04236e3, host.exit reads EXIT=0, and host.log L9070 reads Host: 76 step(s), all green. All seven named node tests are in that log as ... ok (L3637, L3639, L3656, L3764, L3773, L3774, L3782). Apart from the cut, the diff reaches no guest test.

Round 2

  • BLOCKER, the exit's restore and "back in the suite": CLOSED. git grep -i -e revert -e 'back in the suite' on the record exits 1. :104-114 names the seven node tests. The exit's last bullet (:140-143) names the three serve.rs tests as owed. I checked those three lines at ff3b669ad, and they are what the record says they are (serve.rs:563-567, :676-680, :798-843).

  • BLOCKER, the body justifies the cut as a red-test deletion: CLOSED. The body's "The cut" cites Tests and names the holding tests. c6a2861e8's message says the same and does not claim a restore.

  • The seven tests hold what the cut test reached of the node:

    • the places refusal is a_connect_past_the_places_is_refused_and_sends_nothing and a_departed_clients_connection_holds_its_place_until_it_is_cut;
    • the client that left is a_client_that_left_is_finished_by_the_stack;
    • the shutdown-then-leave case is a_writer_that_leaves_after_the_shutdown_leaves_the_stream_to_its_reader;
    • the end that is no pipe is a_pipe_that_is_no_pipe_resets_its_connection and a_refused_watch_resets_only_a_stream_that_holds_the_end;
    • the listener's end is a_wake_the_owners_pipe_refuses_ends_the_listener.

    Its remaining steps exist only in serve.rs: the second receive, settle, and the Sockets::answered arms that turn a kernel watch answer into pipe_gone, pipe_broken, close_listener or end_datagram. serve.rs has no #[test], and the exit records those steps as owed, as round 2 asked. The ConnectRefused::Full → ERR_RESOURCE_EXHAUSTED mapping (serve.rs:157-160) is an exhaustive match a reader can check.

  • The NOTEs, judged under the rulings (issues: servers let a client go on the handshake limit with its request unread, and one program that fills a server's table holds every other client out; netstack_socket_churn is cut #897 (comment)):

    • The title, the unsure bullet and the commit's account of the cut are true; 9f926c672 is gone from the branch.
    • soundserver and surface::Host::accept are in the record and its exit (:16-18, :60-61, :121-122, :131-134). Ruling 1.
    • The supervisor's shared table is stated, and the power port has its own table (:69-71, :135-136). Ruling 2. The table and its lines check out at 1a7464692:system/supervisor/src/main.rs:1213-1248.
    • What a client let go reads follows nginx, with ngx_http_request.c pinned (:137-139). Ruling 3.
    • The isolation hole is filed. Ruling 4. It is judged below.
    • CLOSED, all of them.

BLOCKER

  • issues/one-program-that-fills-a-servers-table-holds-every-other-client-out.md:37-38 — the exit keys every share "by a badge the supervisor mints". That cannot hold for the surface hosts the file lists at :14-16. — Console and terminal each make their own port: system/console/src/main.rs:154-156 and system/terminal/src/main.rs:55-56 both call port::create(). Each moves the connector only into the shell it spawns, and terminal's module doc makes that the point. The supervisor never sees that port and mints nothing on it. Read as written, the exit can be met for those tables only by giving their port to the supervisor, against the capability model ("a process holds exactly what its parent moved into it"). Ruling 4 gives "a per-client share of each table" and says nothing of who mints the key, so the exit narrows the ruling with a design it does not contain. The body's own unsure bullet ("The exit does not choose what the others' badges name") contradicts it. The fix: key the share by a badge that whoever mints the connector writes (the supervisor for the rows it spawns, console and terminal for their own ports), or leave the key out of the exit as the ruling does.

NOTE

  • PR body, "The isolation hole", and ff3b669ad's message ("keyed by a badge the supervisor mints") — both say what the BLOCKER above says, and both become main's record. They change with it.

SEND BACK

…ent out

Found while recording the handshake limit's order, and filed under the
owner's ruling on PR #897. A server's table of connections it has not
yet served is one number for every client: one program that reconnects
each time it is let go keeps the eight limited tables full for as long
as it runs, and holds soundserver's 63 control slots or a surface's 4,
which have no limit, until it hangs up. Once full tables block and the
limit takes a length of 20 s to 60 s, every other client waits behind it.

Only fileserver shares its table out, per session's share. The exit is a
per-client share of each table, keyed by a badge that whoever mints the
connector writes: the supervisor for the servers it spawns, console and
terminal for the ports they create for their own shells. A client past
its share is refused by name, with a host test of each table.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
@Japabu
Japabu force-pushed the wt/toyos-netstack-timeout-issue branch from ff3b669 to f81eea5 Compare October 12, 2026 20:44
@Japabu

Japabu commented Oct 12, 2026

Copy link
Copy Markdown
Collaborator Author

Review, round 4, at f81eea55c, against .claude/agents/reviewer.md on origin/main (1a7464692, still the branch's merge base).

Net lines. +187 −251 (git diff --shortstat origin/main...f81eea55c, 5 files): production 0; tests −251; issues +187. Since ff3b669ad, only one change: f81eea55c rewrites the last commit, and git diff ff3b669ad f81eea55c touches only issues/one-program-that-fills-a-servers-table-holds-every-other-client-out.md (+6 −2).

Host gate at this head. host.head reads f81eea55c0b0543aac16db73dfe999fda60bafd9, host.exit reads EXIT=0, and host.log L8829 reads Host: 76 step(s), all green (evidence 3bb6f68fc). Apart from the cut, the diff reaches no guest test.

Round 3

  • BLOCKER, the exit keys every share by a badge the supervisor mints: CLOSED. At :40-42, the share is now keyed by "a badge that names the client, written by whoever mints the connector: the supervisor for the servers it spawns, console and terminal for their own ports". That is the first form round 3 offered. The exit can be met as written:

    • console and terminal each hold their own port's acceptor (1a7464692:system/console/src/main.rs:154-156, system/terminal/src/main.rs:55-56, both port::create() then Host::serve(acceptor));
    • toyos::port's acceptor mint(&self, badge) (sdk/toyos/src/port.rs:47) lets either one write the badge without handing its port to the supervisor.

    git grep 'Host::serve' finds only those two hosts, so the exit leaves no surface host out. The new sentence at :33-36 cites those lines, and they read as it says. On the supervisor's side, system/supervisor/src/main.rs:410 and :424 create the spawned servers' ports. Ruling 4 asks for "a per-client share of each table", and the exit now adds nothing to it about who mints. It contradicts none of rulings 1-5, nor the ruling on the cut.

  • NOTE, the body and the commit message: CLOSED. f81eea55c's message and the body's "The isolation hole" both state the minter as the exit does. The body's unsure bullet ("says who writes the others' badges, not what they name") now agrees with the record.

BLOCKER

None.

NOTE

None.

LAND

@Japabu
Japabu marked this pull request as ready for review October 12, 2026 20:45
@Japabu
Japabu added this pull request to the merge queue Oct 12, 2026
Merged via the queue into main with commit 5096551 Oct 12, 2026
6 checks passed
@Japabu
Japabu deleted the wt/toyos-netstack-timeout-issue branch October 12, 2026 21:05
Japabu added a commit that referenced this pull request Oct 12, 2026
Japabu added a commit that referenced this pull request Oct 12, 2026
Japabu added a commit that referenced this pull request Oct 12, 2026
…rocode

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
Japabu added a commit that referenced this pull request Oct 13, 2026
Japabu added a commit that referenced this pull request Oct 13, 2026
None of them touches a file this branch changes. #873 deletes
issues/page-global-is-a-decision-nobody-has-made.md, which no file of this
branch cites.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015xhaydXCVRNscoccHYFUWy
Japabu added a commit that referenced this pull request Oct 13, 2026
Japabu added a commit that referenced this pull request Oct 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant