Skip to content

feat(net)!: network modules — contracts, SDK, sim and browser hosts, Rust core (stack A) - #305

Draft
HalfSweet wants to merge 2 commits into
mainfrom
feat/network-v2-a-contracts
Draft

feat(net)!: network modules — contracts, SDK, sim and browser hosts, Rust core (stack A)#305
HalfSweet wants to merge 2 commits into
mainfrom
feat/network-v2-a-contracts

Conversation

@HalfSweet

@HalfSweet HalfSweet commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

Summary

Stack A of 3 for the network modules (supersedes the monolithic #299 as the review unit; B = portable C core, C = ESP-IDF host are stacked on top). This PR is the contract and guest side: the net/ws/httpd specs, the SDK, the ServicePump/headless frame entry, the deterministic sim hosts, the browser dev host and the Rust pocket-net core — plus the review-round fixes that belong to these layers.

Breaking migration (Conventional Commits feat(net)!): @pocketjs/framework/net is a support module now (NetworkError, AbortController/AbortSignal, URL, getNetworkLimits, types); fetch/Headers/Request/Response/BodyStream/serve live in @pocketjs/framework/net/http, the WebSocket client in @pocketjs/framework/net/websocket. The capability id net.http is replaced by network.http.client(.tls) / network.http.server(.tls) / network.websocket.client(.tls). The changelog carries the migration note.

Review-round changes in this PR (reviewer items 1, 3, 8, 9, 10, 11)

  • Network policy is Build Plan truth (1). contracts/spec/network-policy.ts is the typed contract (connect/listen rules, credentials, localNetwork / insecureTransport / allowInvalidTlsForDevelopment), with normalization into a canonical ResolvedNetworkPolicy (v1), the reference matcher and the canonical JSON. Manifest format 3 (pocket: 3, schema/pocket-3.json) adds permissions.network; format 2 stays valid and resolves to deny-all. ResolvedBuildPlan.network is covered by planHash; extractHostBuildInputs() hands hosts network.policyJson (POCKETJS_NETWORK_POLICY) — the exact string every core consumes. The sim hosts enforce a plan policy through the contract matcher. Shared vectors: contracts/spec/vectors/network-policy.json (TS reference, C core in B, Rust core here).
  • Rust core owns the security policy (3). NetPolicy parses the canonical document and enforces localNetwork (address classification shared with C); backends receive a PolicyGate for every resolved address, every redirect hop (spec rewrite table, hop budget, endpoint re-check) and the TLS verification mode; the core classifies literal addresses itself and rejects a response whose URL the gate did not authorize. No bare * anywhere.
  • Hard bounds (8). The clone tee sizes each pull to the remaining room (never past the aggregate limit); the browser host reads through a BYOB reader sized to the queue's free space.
  • Spec-pinned wire semantics (9). NET_METHODS_FORBIDDEN (+TRACK), HTTP_CORE_OWNED_REQUEST_HEADERS, HTTP_BODYLESS_STATUS, HTTP_NULL_BODY_STATUS, HTTP_REDIRECT_*; generated into spec.h, spec.rs and the new hosts/web/net-spec.js (drift-guarded); private copies removed; contracts/spec/vectors/http-semantics.json runs on SDK, sim, browser host, C core (B) and Rust core.
  • One frame prelude (10). runFramePrelude replaces the four hand-typed copies (Solid, Vue Vapor, Octane, headless).
  • Changelog / breaking (11). Unreleased entry + ! title. Also: framework/src/net/url.ts carried raw NUL/DEL bytes (git treated it as binary) — escapes now.

Validation

bunx tsc --noEmit, bun tests/contract.ts (spec.rs / spec.h / net-spec.js / package exports drift), bun tools/test.ts 11/11 stages green (473 unit tests incl. network-policy, net-policy-hosts, http-semantics), cargo test -p pocket-net 11 tests (gate, literal classification, both shared vectors, mount).

Stack

  1. this PR (A) — contracts + SDK + sim/web + Rust core
  2. feat(net): portable C network core, POSIX driver, TLS providers (stack B) #306engine/net portable C core, POSIX driver, OpenSSL/ESP-TLS providers, conformance harness
  3. feat(esp-idf): ESP-IDF network host for AtomS3R and Tab5 (stack C) #307 — ESP-IDF host, board bring-up, smoke firmware with the plan-derived policy, hardware record

…im and browser hosts, Rust core (stack A)

The contract and guest side of the network stack (stacked PR A of three;
B adds the portable C core and the POSIX/TLS drivers, C the ESP-IDF host):

- contracts/spec/{net,ws,httpd}.ts: the net spec at v2 (ops 6–9 readInto /
  limits / write / endBody, op 2 retired, streaming headers / readable /
  end / error events, the shared error vocabulary with the tls_* codes) and
  the new ws and httpd module specs; gen-rust emits net/ws/httpd, the new
  gen-c emits engine/net/include/pocketjs/net/spec.h (the generated header
  rides here, the core that consumes it lands in B); tests/contract.ts
  byte-compares every mirror.
- @pocketjs/framework/net is a support module (NetworkError,
  AbortController/AbortSignal, URL, getNetworkLimits, shared types);
  net/http (fetch, Headers, Request, Response, BodyStream, serve) and
  net/websocket (connect) carry the modules. A per-module guest binding
  drains one poll per tick from the framework service pump, settles
  Promises inside that pump and copies bodies through readInto.
  @pocketjs/framework/headless runs the frame transaction without a UI.
- contracts/spec/platforms.ts replaces net.http with the role-split
  network.http.client(.tls) / network.http.server(.tls) /
  network.websocket.client(.tls) ids; no stock target advertises them.
- hosts/sim/{net,httpd,ws}.ts are the deterministic hosts; hosts/web/net.js
  moves to the streaming v2 contract; engine/crates/pocket-net is rewritten
  to the v2 boundary over an HttpClientBackend.

BREAKING CHANGE: `@pocketjs/framework/net` no longer exports fetch /
NetError / PocketResponse — import fetch from `@pocketjs/framework/net/http`
(streaming Response, NetworkError); the capability id `net.http` is replaced
by `network.http.client` and its siblings.
…ntics, hard bounds, one frame prelude, Rust gate

The review fixes for the contract/SDK/host side (stack A):

- Network policy is a typed contract and plan truth (reviewer item 1):
  contracts/spec/network-policy.ts (connect / listen rules, credentials,
  switches; normalization into a canonical ResolvedNetworkPolicy v1; the
  reference matcher; canonical JSON). Manifest format 3 adds
  permissions.network (contracts/schema/pocket-3.json; format 2 stays
  valid and resolves to deny-all). ResolvedBuildPlan.network is covered by
  planHash; extractHostBuildInputs projects planHash, features and the
  canonical policy JSON (POCKETJS_NETWORK_POLICY). The sim hosts enforce an
  optional plan policy through the contract matcher (connect, listen,
  redirect re-check). contracts/spec/vectors/network-policy.json pins
  parse and match decisions for every implementation.
- Wire semantics live in the spec (item 9): NET_METHODS_FORBIDDEN carries
  TRACK; HTTP_CORE_OWNED_REQUEST_HEADERS, HTTP_BODYLESS_STATUS,
  HTTP_NULL_BODY_STATUS, HTTP_REDIRECT_* join it; gen-c / gen-rust / the
  new gen-web (hosts/web/net-spec.js, drift-guarded) emit them; the SDK,
  sim and browser host drop their private copies; the sim method check is
  case-insensitive like the cores. contracts/spec/vectors/http-semantics.json
  runs on the SDK, the sim, the browser host, the C core (B) and the Rust core.
- Hard bounds (item 8): teeBody sizes each pull to the remaining room so a
  clone's lagging branch never exceeds the aggregate limit; the browser dev
  host reads through a BYOB reader sized to the queue's free space.
- runFramePrelude (item 10): one definition of clock → input latches →
  service pumps → effects for the Solid, Vue Vapor, Octane and headless
  entries.
- Rust parity (item 3): the core owns the policy — NetPolicy parses the
  canonical document with the reference matcher (localNetwork enforced, no
  bare wildcard), backends receive a PolicyGate for every resolved address,
  redirect hop (spec rewrite table, hop budget, endpoint re-check) and TLS
  verification mode, the core classifies literal addresses and rejects a
  response from a URL the gate did not authorize; the shared vectors run in
  its tests.
- Changelog (item 11): an Unreleased entry names the net change a breaking
  migration and introduces format 3. url.ts carried raw NUL/DEL bytes (git
  saw it as binary); they are escapes now.
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