Skip to content

feat(wasm): instance-owned outbound TCP for core modules - #543

Merged
wolfy-j merged 5 commits into
mainfrom
feat/pyodide-wasm-runtime
Aug 30, 2026
Merged

feat(wasm): instance-owned outbound TCP for core modules#543
wolfy-j merged 5 commits into
mainfrom
feat/pyodide-wasm-runtime

Conversation

@skhaz

@skhaz skhaz commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Why

Core wasm32-wasip1 modules cannot import component-model wasi:sockets, so language runtimes such as CPython need a small raw TCP ABI to use their own TLS and protocol stacks.

Design

  • Adds the versioned core import wippy:runtime/socket@0.1.0 with connect, send, recv, and close.
  • Uses the existing netapi.Service. The shared secure service owns socket.connect, socket.private_ip, selected-overlay routing, remote DNS behavior, and fail-closed lookup. The WASM host does not resolve overlays or invent permissions.
  • Stores connections in the executing WASM instance resource table. Handles and capacity are isolated per instance; instance close destroys all remaining connections.
  • Uses limits.max_open_sockets and limits.socket_timeout_ms with safe defaults. Connect, send, and receive are bounded; cancellation closes the connection even when an overlay connection does not implement deadlines.
  • Keeps synchronous raw calls. No asyncify transformation is added to large core guests.

The function/process option pipeline remains the only overlay selector: per-call options.network, entry options, and the application default are resolved into the call frame before execution.

Lifecycle prerequisite

Depends on wippyai/wasm-runtime#15, now merged as e9b09350cd7c. That change gives every core/component instance an owned host-resource table and drains component resource destructors before closing core modules.

Existing pool retirement remains the lifecycle owner: update, delete, dynamic uninstall, and manager stop call pool stop; pool stop closes its processes; process close closes the instance; instance close drops the connections.

WASI sockets correction

This also folds #611 into the same WASM assembly: finish-connect and accept now return one record plus the network error, matching canonical ABI lowering. A committed Rust wasm32-wasip2 fixture imports the stock socket interfaces and must bind in CI.

Verification

  • Touched-package tests: green.
  • Touched-package lint: 0 issues.
  • Targeted race suite across network, core sockets, WASI sockets, engine, function/process managers, and boot registration: green.
  • Resource lifecycle tests cover destructor execution, instance isolation, idempotent close, warm-process close, foreign-handle rejection, per-instance limits, overlay routing, cancellation, and configured timeout against a connection that ignores deadlines.
  • Repository-wide tests passed except the unrelated RabbitMQ readiness integration (service/queue/amqp). Repository-wide lint reports four existing findings in untouched registry/bootconfig files.

@skhaz

skhaz commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

Proof that the wasm guest obeys the same network rules as Lua

A guest was previously dialed with a plain net.Dialer, so it ignored the network options every host-side caller obeys. In an application routed through an overlay that is a leak: the guest reaches clearnet, exposing the DNS lookup and the target address the overlay exists to hide.

The dial now resolves exactly as runtime/lua/modules/httpclient and service/http/client do — netapi.GetDefaultNetwork on the frame, which already carries the effective value after per-call options.network, per-entry meta.options.network and the app default have been merged. The registry is read from the call context, not captured at boot, so a guest follows the network its own frame selected.

Rule-by-rule

Rule Lua / HTTP dispatcher wasm guest (this PR)
Overlay selection source netapi.GetDefaultNetwork(ctx) same
network.select permission required required
Overlay requested, registry missing refused, no clearnet fallback refused, no clearnet fallback
Local DNS for overlay targets skipped (would leak the hostname) skipped
Private-address policy http_client.private_ip same

Test output

=== RUN   TestOverlayNetworkIsUsedWhenSelected
--- PASS: TestOverlayNetworkIsUsedWhenSelected (0.00s)
=== RUN   TestOverlayRequestedWithoutRegistryIsRefused
    refused: overlay network "network:tor" requested but no network registry is configured
--- PASS: TestOverlayRequestedWithoutRegistryIsRefused (0.00s)
=== RUN   TestPrivateAddressNeedsPermission
    refused: not allowed: private IP 127.0.0.1
--- PASS: TestPrivateAddressNeedsPermission (0.00s)
=== RUN   TestOverlaySelectionNeedsPermission
    refused: not allowed: network network:tor
--- PASS: TestOverlaySelectionNeedsPermission (0.00s)
ok      github.com/wippyai/runtime/runtime/wasm/host/wippy/hosts/coresock

TestOverlayNetworkIsUsedWhenSelected asserts the overlay dialer is the one actually called, with the address it received; TestOverlaySelectionNeedsPermission asserts that when the permission is denied the overlay dialer is not called and the dial fails, rather than being downgraded.

Also in this push

The two lint findings from the earlier run are fixed: a fieldalignment report on an anonymous struct (replaced with explicit registration calls) and a British spelling flagged by misspell. ./runtime/wasm/... and ./boot/components/runtime/... are green, and golangci-lint reports 0 issues for the touched packages.

@wolfy-j
wolfy-j force-pushed the feat/pyodide-wasm-runtime branch from 82a276c to a96d89a Compare August 30, 2026 22:26
@wolfy-j wolfy-j changed the title feat(wasm): outbound TCP for core modules (wippy:sock/tcp) feat(wasm): instance-owned outbound TCP for core modules Aug 30, 2026
@wolfy-j
wolfy-j merged commit cc1522b into main Aug 30, 2026
5 checks passed
@wolfy-j
wolfy-j deleted the feat/pyodide-wasm-runtime branch August 30, 2026 23:09
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