docs: story 35 — net runtime seams (deadlines, unix sockets, peer addr)

- owns the framework ledger's three seam rows; deadlines compose with
  the arc's park plane (POLL_ADD+TIMEOUT fork recorded); framework
  knobs explicitly out of scope; stalled-client soak in acceptance
- board + table rows (held seqs bumped); pairs naturally with 24

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
shoney.arickathil 2026-08-22 17:37:12 +02:00
parent 38457973c3
commit 526a837102
3 changed files with 117 additions and 8 deletions

View file

@ -244,6 +244,7 @@ that sequences its tasks. Read one, approve, then the next starts.
| 32 | [WAL checkpoint](language-runtime-database/refine/32-wal-checkpoint.md) | ⬜ last in chain, after 23 — disk reclamation + bounded replay (story written 2026-08-21) |
| 33 | [Single-file store](language-runtime-database/refine/33-single-file-db.md) | ⬜ off-chain, small — `WO_DATA=<path>.db` file form; driver-only (story written 2026-08-22) |
| 34 | [Crypto builtins](language-runtime-database/refine/34-crypto-builtins.md) | ⬜ off-chain but GATES 24 (WS handshake needs SHA-1) — digests + HMAC as vector-verified C builtins (story written 2026-08-22) |
| 35 | [net runtime seams](language-runtime-database/refine/35-net-runtime-seams.md) | ⬜ off-chain — fd deadlines on the park plane, Unix sockets, peer address; owns the ledger's three 🔧 rows (story written 2026-08-22) |
| 20 | [Cross-program tables](language-runtime-database/hold/20-cross-program-tables.md) | ⏸ hold (2026-08-21); channel done (branch ipc-attach keeps its manifest) |
| 21 | [Keypair attach auth](language-runtime-database/hold/21-keypair-attach-auth.md) | ⏸ hold (2026-08-21); crypto+handshake done (branch keypair-auth keeps its manifest) |
| 25 | [HTTP service layer](../superpowers/plans/2026-08-01-http-service-layer.md) | ⏸ hold (2026-08-21) — story file removed; the plan doc remains |

View file

@ -109,14 +109,15 @@ still pending IS the runtime-concurrency chain; order:
| 20 | 32 | [WAL checkpoint](refine/32-wal-checkpoint.md) | **NEW 2026-08-21** (stage-3 guarantee refinement found the hole) — the WAL is append-only forever: snapshot + truncate reclaims disk and bounds replay time; every durability guarantee byte-identical; crash mid-checkpoint recovers from the previous snapshot + full tail. After 23 (composes with group-commit); RAM slot-reuse already contracted in `04-db-binding.md`. |
| 21 | 33 | [Single-file store](refine/33-single-file-db.md) | **NEW 2026-08-22** — `WO_DATA=<path>.db`: a file path IS the wal (the store already lives in exactly one file; this makes the surface say so). Driver-only, independent of the chain; composes with 32's rename-swap. |
| 22 | 34 | [Crypto builtins](refine/34-crypto-builtins.md) | **NEW 2026-08-22** — SHA-1/SHA-256/HMAC-SHA256 as C builtins over Bytes (no bitwise ops in the language, hand-rolled per doctrine, vector-verified). GATES 24's WS handshake; digest floor for held 21 and the ETag row. |
| 23 | 25 | [HTTP service layer](../../superpowers/plans/2026-08-01-http-service-layer.md) | `service` blocks lower onto the framework (after 9b + 20 by their own precedence notes). **HELD 2026-08-21** — story file removed; the plan doc remains. *(was 10)* |
| 24 | 18 | [framework v2: memory-rich features](hold/18-memory-db-features.md) | spec+plan approved: TTL cache, @table flags, durable job queue, `transaction { }` over the WAL's staged batch. **Demoted from seq 14**: more surface on a framework with one consumer, and the cache still stores `Text` because there are no generics |
| 25 | 27 | [Query grammar corpus](hold/27-query-grammar-corpus.md) | grow the query grammar from real corpora; likely collapses to "confirm `len(query)` + add `exists`"; precedes 28. *(was 9g)* |
| 26 | 26 | [Blue-green deploy](hold/26-blue-green-deploy.md) | two VM slots, in-runtime compile, atomic switch, resident rollback (plan authored after 9 + 25). *(was 12)* |
| 27 | 20 | [Cross-program tables](hold/20-cross-program-tables.md) | attach to a running program's database over local IPC; owner stays the single writer (channel half-built). **Demoted from seq 16**: new distribution surface while there is no TLS, no crypto, and the multi-shard DB still traps. *(was 9c)* |
| 28 | 21 | [Keypair attach auth](hold/21-keypair-attach-auth.md) | program identity is a keypair; mutual challenge–response at attach (crypto half-built; plan folds into 20's). **Demoted with 20** — and it needs crypto primitives that do not exist. *(was 9d)* |
| 29 | 28 | [skillhost host workload](hold/28-skillhost-host-workload.md) | host-shaped driving workload naming runtime gaps — demoted with the framework goal. *(was 14)* |
| 30 | 29 | [Compile-time metaprogramming](hold/29-compile-time-metaprogramming.md) | `@derive(...)` from class-table metadata; held with the parked drain by the 2026-08-08 scope directive. *(was 13)* |
| 23 | 35 | [net runtime seams](refine/35-net-runtime-seams.md) | **NEW 2026-08-22** — the ledger's three 🔧 rows owned: fd deadlines composing with the park plane, Unix-socket listeners, peer address (trusted-proxy check). Framework knobs stay framework slices; pairs naturally with 24 (dead-client eviction). |
| 24 | 25 | [HTTP service layer](../../superpowers/plans/2026-08-01-http-service-layer.md) | `service` blocks lower onto the framework (after 9b + 20 by their own precedence notes). **HELD 2026-08-21** — story file removed; the plan doc remains. *(was 10)* |
| 25 | 18 | [framework v2: memory-rich features](hold/18-memory-db-features.md) | spec+plan approved: TTL cache, @table flags, durable job queue, `transaction { }` over the WAL's staged batch. **Demoted from seq 14**: more surface on a framework with one consumer, and the cache still stores `Text` because there are no generics |
| 26 | 27 | [Query grammar corpus](hold/27-query-grammar-corpus.md) | grow the query grammar from real corpora; likely collapses to "confirm `len(query)` + add `exists`"; precedes 28. *(was 9g)* |
| 27 | 26 | [Blue-green deploy](hold/26-blue-green-deploy.md) | two VM slots, in-runtime compile, atomic switch, resident rollback (plan authored after 9 + 25). *(was 12)* |
| 28 | 20 | [Cross-program tables](hold/20-cross-program-tables.md) | attach to a running program's database over local IPC; owner stays the single writer (channel half-built). **Demoted from seq 16**: new distribution surface while there is no TLS, no crypto, and the multi-shard DB still traps. *(was 9c)* |
| 29 | 21 | [Keypair attach auth](hold/21-keypair-attach-auth.md) | program identity is a keypair; mutual challenge–response at attach (crypto half-built; plan folds into 20's). **Demoted with 20** — and it needs crypto primitives that do not exist. *(was 9d)* |
| 30 | 28 | [skillhost host workload](hold/28-skillhost-host-workload.md) | host-shaped driving workload naming runtime gaps — demoted with the framework goal. *(was 14)* |
| 31 | 29 | [Compile-time metaprogramming](hold/29-compile-time-metaprogramming.md) | `@derive(...)` from class-table metadata; held with the parked drain by the 2026-08-08 scope directive. *(was 13)* |
| ✅ | 17 | [library projects + `internal/`](done/17-library-projects-internal.md) | **LANDED 2026-08-20** — `kind = "library"` + entry-less check mode (retires the `--emit` workaround) and Go's `internal/` rule as WO-E108 at the consumer's `use`; driver-only, VM/GC untouched. `just web-app` 26/0 |

View file

@ -0,0 +1,107 @@
---
iteration: "35"
status: refine
---
# Iteration 35 — `net` runtime seams: timeouts, Unix sockets, peer address
> Format: `product/story-iteration-template`. Part of
> [Story — one language, one runtime, one database, one binary](../00-story.md).
>
> **Inserted 2026-08-22** — the framework ledger's three 🔧 rows get one
> owner: "Read/write/idle timeouts — `net` has no timeout surface",
> "Unix socket binding — `net.listen` is TCP-only", and "Trusted-proxy
> client IP — needs a peer-address runtime seam". Each is a small
> builtin-surface addition; the framework knobs built ON them stay
> framework slices. Off the concurrency chain; no chain item depends on
> it, but a production-shaped deployment (proxy in front, sockets not
> ports, slow-client defense) needs all three.
## Why this iteration exists
The framework cannot defend against a slow client (no read deadline —
one stalled socket parks a fiber forever), cannot sit behind a
same-host proxy the idiomatic way (Unix socket beats a loopback port),
and cannot TRUST `X-Forwarded-For` (parsing is expressible in `.wo`
today, but verifying the peer actually IS the proxy needs the peer's
address, which no builtin exposes). All three are runtime seams by
nature: the information or mechanism lives at the fd level.
## Goals
- **Deadlines on parked net I/O.** A read/accept/write that would park
can carry a deadline; expiry resumes the fiber with a distinguishable
timeout result (nil-or-trap decided by the spec, consistent with the
stdlib's nil-vs-trap contracts). On serving shards this composes with
the existing plane — a park is ALREADY a POLL_ADD or TIMEOUT
submission (arc T4); the seam arms both and takes whichever fires.
Program mode gets the same surface over blocking syscalls.
- **Unix-domain listeners and connections** beside the TCP ones — same
accept/read/write/close builtins afterward (an fd is an fd; only the
bind/connect shape differs).
- **The peer's address, readable** — for an accepted connection, enough
to answer "is this my trusted proxy?" (address + family; port where
meaningful). The framework's trusted-proxy middleware then becomes a
pure-`.wo` candidate slice.
- **The framework knobs are explicitly NOT here** — timeout defaults,
proxy allowlists, socket-path config all live in framework slices
that consume these seams.
## Acceptance Criteria (draft — the spec refines)
- **Given** a fiber reading a socket whose peer sends nothing, **when**
the declared deadline expires, **then** the fiber resumes with the
timeout result (never a hang), other fibers having run throughout
(TID-verified), and the fd is still usable or cleanly closed per the
spec's stated semantics.
- **Given** a listener on a Unix socket path, **when** a client
connects and exchanges bytes, **then** the whole existing net surface
works unchanged over it, and the log-watcher/web-app gates stay
byte-identical on TCP.
- **Given** an accepted connection, **when** the handler asks for the
peer address, **then** loopback TCP and Unix-socket peers are both
identifiable, and the answer round-trips into the trusted-proxy
check's comparison.
- **Given** the full battery plus a soak with deliberately stalled
clients, **when** it runs, **then** zero leaked fds and flat RSS —
timeouts must CLEAN UP, not merely return.
## Out Of Scope
- Framework policy (default timeout values, proxy allowlist shape,
keep-alive idle policy) — framework slices on top.
- TLS, h2c — unchanged owners (proxy; parked behind 23).
- Connect-side timeouts for outbound clients beyond what the deadline
seam gives free — no workload asks yet.
- Cancellation as a general mechanism — iteration 31's
request/response + timers own actor-level cancellation; this is
strictly fd-level deadlines.
## Info
Forks the spec must settle:
1. **Timeout result shape**: nil result vs a distinguishable trap —
must follow the stdlib's existing nil-vs-trap doctrine
(`07`-series contracts; a timeout is an EXPECTED outcome, which
argues nil).
2. **Deadline plumbing on the plane**: one park may need BOTH a
POLL_ADD and a TIMEOUT in flight (io_uring linked ops vs two
submissions + first-wins cancel; epoll fallback = the existing
deadline scan). The park protocol contract
([`03-concurrency-coroutines.md`](../../../plan/oop-vm/03-concurrency-coroutines.md))
gains the rule.
3. **Surface shape**: per-call deadline argument vs per-fd setting
(`net.set_deadline(fd, ms)`); leaning per-call — no hidden fd state,
matches the no-coloring doctrine.
4. **Unix-socket path semantics**: unlink-before-bind? stale-socket
handling on restart (the never-stopping-runtime doctrine says a
restart must not need manual cleanup).
## Proposed Solution
Brainstorm → small spec settling the four forks → implement in the
`time.ticks`/34 shape (builtin ids + park.c deadline arming + contract
rows + fixtures incl. a stalled-client corpus case). Independent of the
chain; natural pairing is right before or with iteration 24 (chat wants
read deadlines for dead-client eviction even before lifecycle timers).