Skip to content

ext_net: raw TCP sockets as tape-recorded nondeterministic inputs (#414) - #788

Merged
InauguralPhysicist merged 1 commit into
mainfrom
feat/414-ext-net
Aug 1, 2026
Merged

ext_net: raw TCP sockets as tape-recorded nondeterministic inputs (#414)#788
InauguralPhysicist merged 1 commit into
mainfrom
feat/414-ext-net

Conversation

@InauguralPhysicist

Copy link
Copy Markdown
Collaborator

Part of #414 (TCP half; UDP and the liferaft real-socket demo stay open there).

What

src/ext_net.c — an opt-in extension in ext_http's shape (EIGENSCRIPT_EXT_NET=1, in no default build): net_listen / net_port / net_accept / net_dial / net_recv / net_send / net_close, with poll-based timeouts. Sockets are HANDLE_NET rows in the process handle table, drained at exit.

The tape contract (the definition of done, not a follow-up)

Acceptance

$ EIGS_TRACE=t.tape  eigenscript examples/net_echo.eigs > a
$ EIGS_REPLAY=t.tape eigenscript examples/net_echo.eigs > b
$ diff a b        # byte-identical, no network touched

Suite section [125] (probe-gated, runs under make net and make asan-http in CI): 25 loopback echo checks — a single-threaded program is both ends via the listen backlog — plus record→replay byte-diff and a pinned 17-record tape accounting check.

Gates

  • release 3280/3280 · net 3308/3308 · ASan 3284/3284 · asan-http(+net) 3408/3408 — all 0 failed, leak tally at floor 0
  • ext_net now compiles into make asan-http, so the extension stays sanitizer-covered in CI
  • http / gfx / lsp / freestanding compile checks green

Residual (noted for the tape-first review)

  • Handle structs aren't refcounted: a cross-thread net_close racing a blocking net_recv is the same exposure class as existing Channel/Thread handles. Single-threaded use is the v1 contract.
  • getaddrinfo in net_dial blocks without a timeout (DNS is resolved before the nonblocking-connect timeout applies).

🤖 Generated with Claude Code

Seven builtins — net_listen / net_port / net_accept / net_dial /
net_recv / net_send / net_close, with poll-based timeouts — behind
make net (EIGENSCRIPT_EXT_NET=1, in no default build; also compiled
into make asan-http so the sanitizer gate covers the extension).

Every nondeterministic outcome (accepted connections, received bytes,
bytes-sent counts, dial results, kernel-assigned ephemeral ports)
enters through TRACE_NONDET_TAKE/RECORD, so a recorded session
replays byte-identically with no network present:

  $ EIGS_TRACE=t.tape  eigenscript examples/net_echo.eigs > a
  $ EIGS_REPLAY=t.tape eigenscript examples/net_echo.eigs > b
  $ diff a b            # identical; strace: 0 socket syscalls

Replay-determinism rule (the #601 lesson): environment failures are
recorded VALUES — null / -1 / empty buffer — never live-path-only
raises, so a catch cannot desync the N-record stream; argument-shape
errors raise deterministically before the tape boundary. net_send is
a suppressed recorded write (the mkdir rule), documented in TRACE.md
as the deliberate contrast to the #148 subprocess family.

Sockets live in the process handle table (HANDLE_NET) and are closed
by handle_table_drain at exit. Suite section [125] (probe-gated) runs
25 loopback echo checks plus the definition-of-done: record → replay
byte-diff and a pinned 17-record tape accounting. TCP only for now —
UDP stays open in #414.

Gates: release 3280/3280, net 3308/3308, ASan 3284/3284,
asan-http(+net) 3408/3408, leak tally 0; http/gfx/lsp/freestanding
compile checks green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 1, 2026 20:36

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an opt-in ext_net extension that exposes raw TCP sockets while preserving EigenScript’s trace/replay determinism by routing all nondeterministic outcomes through the trace tape (TAKE/RECORD), and integrates it into builds, docs, CI, and the test suite.

Changes:

  • Introduces src/ext_net.c (+ private header) implementing net_listen/net_port/net_accept/net_dial/net_recv/net_send/net_close with poll-based timeouts and trace-tape wrapping.
  • Adds a dedicated test file plus a new probe-gated suite section that verifies record→replay byte identity and a pinned N-record count.
  • Wires the extension into build targets, CI, lint’s builtin-name surface, and user docs/CHANGELOG.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/test_net.eigs New single-process loopback tests for the net_* builtins, including timeout/EOF/value behavior expectations.
tests/run_all_tests.sh Adds probe-gated section [125] running net tests and enforcing record→replay output equality + pinned tape accounting.
src/lint.c Extends lint’s builtin-name sets to include EIGS_NET_BUILTINS.
src/ext_net.c New extension implementation: TCP sockets + tape-wrapped nondeterminism + handle-table integration.
src/ext_net_internal.h Private extension header defining EigsNetSock for handle-table storage/drain.
src/ext_names.h Adds the EIGS_NET_BUILTINS macro list.
src/eigenscript.h Adds EIGENSCRIPT_EXT_NET flag and HANDLE_NET handle type.
src/builtins.c Registers net builtins when enabled; drains HANDLE_NET rows at teardown.
README.md Documents make net build target.
Makefile Adds net target; includes ext_net in full build and asan-http build.
examples/net_echo.eigs New example program demonstrating record/replay with no network on replay.
docs/TRACE.md Documents ext_net’s tape contract and replay suppression of net_send.
docs/BUILTINS.md Documents the new net_* builtins and their semantics.
CHANGELOG.md Adds an Unreleased entry describing ext_net and its trace/replay guarantees.
.github/workflows/ci.yml Adds make net + full-suite run for the net variant.
Suppressed comments (3)

src/ext_net.c:226

  • net_dial doesn’t check fcntl(F_GETFL) / fcntl(F_SETFL) results when switching the socket to nonblocking. If either fails, connect() may block indefinitely (breaking the timeout guarantee), or the fd may end up in a mode the later send/recv paths don’t handle (e.g., EAGAIN).
    int flags = fcntl(fd, F_GETFL, 0);
    fcntl(fd, F_SETFL, flags | O_NONBLOCK);
    int rc = connect(fd, res->ai_addr, res->ai_addrlen);

src/ext_net.c:243

  • net_dial restores the original fd flags with fcntl(fd, F_SETFL, flags) but doesn’t check for failure. If the restore fails, subsequent net_send/net_recv may behave unexpectedly (e.g., nonblocking EAGAIN) since those paths assume a blocking socket plus poll.
    fcntl(fd, F_SETFL, flags);

src/ext_net.c:259

  • net_recv parses max_bytes and timeout_ms with net_num_at, which means a non-numeric timeout_ms silently becomes -1 (infinite block) and a non-numeric max_bytes becomes 0 (then raises EK_VALUE). For consistency with the contract that argument-shape errors raise deterministically before the tape boundary, these should be validated as numbers explicitly.
    int max = net_num_at(arg, 1, 0);
    int timeout_ms = net_num_at(arg, 2, -1);

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/ext_net.c
Comment on lines +175 to +179
if (arg && arg->type == VAL_LIST) {
if (!net_require_list(arg, 2, 2, "net_accept")) return make_null();
timeout_ms = net_num_at(arg, 1, -1);
arg = arg->data.list.items[0];
}
Comment thread src/ext_net.c
Comment on lines +209 to +213
int port = net_num_at(arg, 1, -1);
int timeout_ms = net_num_at(arg, 2, -1);
TRACE_NONDET_TAKE("net_dial");
if (port < 0 || port > 65535) TRACE_NONDET_RECORD("net_dial", make_null());

@InauguralPhysicist
InauguralPhysicist merged commit e6476f4 into main Aug 1, 2026
19 checks passed
@InauguralPhysicist
InauguralPhysicist deleted the feat/414-ext-net branch August 1, 2026 20:55
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.

2 participants