Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
- [`node:string_decoder`](./interop/nodejs-builtins/supported-modules/string-decoder.md)
- [`node:test`](./interop/nodejs-builtins/supported-modules/test.md)
- [`node:timers`](./interop/nodejs-builtins/supported-modules/timers.md)
- [`node:tls`](./interop/nodejs-builtins/supported-modules/tls.md)
- [`node:tty`](./interop/nodejs-builtins/supported-modules/tty.md)
- [`node:url`](./interop/nodejs-builtins/supported-modules/url.md)
- [Troubleshooting]()
Expand Down
2 changes: 1 addition & 1 deletion docs/src/interop/nodejs-builtins.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ set of coordinated shims:
`node:crypto`, `node:dgram`, `node:http2`,
`node:perf_hooks`, `node:repl`, `node:stream`,
`node:stream/promises`, `node:stream/web`,
`node:tls`, `node:util`, `node:util/types`, `node:v8`, `node:vm`, `node:wasi`,
`node:util`, `node:util/types`, `node:v8`, `node:vm`, `node:wasi`,
`node:worker_threads`, and `node:zlib`.

#### Future composition
Expand Down
16 changes: 13 additions & 3 deletions docs/src/interop/nodejs-builtins/supported-modules/http.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,18 @@ construction immediately because outgoing-handler cannot listen for arbitrary
connections.

For direct servers, instantiate with a provider bound to that component's
callback dispatcher. For example, after transpiling with
`--instantiation async --map jco:node/http@0.1.0=http-host`:
callback dispatcher. The provider is asynchronous, so select JSPI and its async
imports explicitly; instantiation output keeps the WIT import names and nothing
selects them for you. For example, after transpiling with:

```sh
jco transpile component.wasm -o out --instantiation async \
--async-mode jspi --async-exports '*' \
--async-imports 'jco:node/http@0.1.0#request' \
'jco:node/http@0.1.0#[method]server.listen' \
'jco:node/http@0.1.0#[method]server.close' \
'jco:node/http@0.1.0#[method]server.get-connections'
```

```js
import { instantiate } from './component.js';
Expand All @@ -51,7 +61,7 @@ import { createHttpHost } from '@bytecodealliance/jco-std/wasi/0.2.x/node/24.x.x

let instance;
const imports = new WASIShim().getImportObject();
imports['http-host'] = createHttpHost(() => instance.httpCallbacks);
imports['jco:node/http'] = createHttpHost(() => instance.httpCallbacks);
instance = await instantiate(undefined, imports);
// Await application exports that create or control servers.
await instance.start();
Expand Down
7 changes: 5 additions & 2 deletions docs/src/interop/nodejs-builtins/supported-modules/http2.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,11 @@ jco componentize component.js --wit wit --bundle \
| `wasi-sockets` | Cleartext prior-knowledge HTTP/2 (`h2c`) clients and TCP servers, with guest-side framing, HPACK, settings, ping, reset, and stream/connection flow control. |
| `wasi-http` | Rejects sessions and servers: outgoing-handler cannot expose observable Node sessions, stream control, or arbitrary inbound listeners. |

By default, the provider rejects both `connect()` and server construction with
`ERR_JCO_HTTP2_ADAPTER_REQUIRED`.
By default, cleartext operations fail with `ERR_JCO_HTTP2_ADAPTER_REQUIRED`;
secure operations first require `jco:node/tls` and fail with
`ERR_JCO_TLS_ADAPTER_REQUIRED` when it is denied. Direct secure sessions and
servers obtain one-use configuration handles from the TLS provider. Bind it
with `createHttp2Host(() => instance.http2Callbacks, tls)`.

`direct` mode models sessions, streams, and servers as typed host-owned WIT
resources, with a passthrough implementation to NodeJS underneath. The WIT
Expand Down
95 changes: 27 additions & 68 deletions docs/src/interop/nodejs-builtins/supported-modules/https.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,71 +15,30 @@ key as Node, so option bags pool the way they would natively. Requests reject
non-`https:` protocols with `ERR_INVALID_PROTOCOL` and elide `:443` from the
authority, and `https.get()` ends the request itself.

TLS crosses the component boundary as a typed `tls-options` record on the
`jco:node/http@0.1.0` request and server options. It carries the serializable
subset of Node's `tls.connect` / `tls.createServer` options: `key`, `cert`,
`pfx`, `passphrase`, `ca`, `crl`, `dhparam`, `ciphers`, `ecdhCurve`, `sigalgs`,
`minVersion`, `maxVersion`, `secureProtocol`, `secureOptions`,
`sessionIdContext`, `honorCipherOrder`, `ALPNProtocols`, `servername`,
`rejectUnauthorized`, and `requestCert`. Material fields stay lists, so a
`key: [rsa, ecdsa]` bundle reaches the host intact. Options with no typed
representation -- `checkServerIdentity`, `SNICallback`, `ALPNCallback`,
`pskCallback`, `secureContext`, `session`, `ticketKeys`, and the OpenSSL engine
options -- throw `ERR_JCO_UNSUPPORTED_NODE_API` naming the option rather than
being dropped.

| Value | `node:https` behaviour |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `direct` | Clients and servers. The opt-in Node provider routes `https` requests to `node:https.request` with the carried TLS options, and a server carrying a `tls` record to `node:https.createServer`, so the host's own TLS stack terminates the connection. |
| `wasi-sockets` | Verified clients over the existing TCP streams. TLS connections implicitly require `wasi:tls`, imported automatically for `node:https`. HTTPS servers are unsupported by the pinned client-only draft. |
| `wasi-http` | Clients only, with the `HTTPS` scheme. `wasi:http/outgoing-handler` owns certificate validation, so any per-request TLS option is refused; servers are rejected as for `node:http`. |

TLS support is part of the `wasi-sockets` implementation, which uses the
`wasi:tls` host capability for TLS connections. Explicitly grant it when transpiling:

```sh
jco transpile component.wasm -o out \
--map 'wasi:tls/types@0.2.0-draft=@bytecodealliance/jco-std/wasi/0.2.x/node/24.x.x/tls/host/node'
```

Sockets and TLS share `wasi:io@0.2.12` stream resources directly. Without this
opt-in, HTTPS fails before connecting, with no plaintext fallback. Plain HTTP
needs no TLS capability.
The Node provider uses `node:tls` over the supplied TCP streams, system trust,
hostname verification, and HTTP/1.1 ALPN. Hosts needing private trust can map the
TLS interface to a module exporting:

```js
import { createTlsProvider } from '@bytecodealliance/jco-std/wasi/0.2.x/node/24.x.x/tls/host/node';
export const { ClientHandshake, ClientConnection, FutureClientStreams, isAvailable } = createTlsProvider({
ca: [trustedCaPem],
handshakeTimeoutMs: 10_000,
});
```

Based on upstream [`WebAssembly/wasi-tls` at `6781ae26084100c0628ef72cc44e4517c6c48ae5`](https://github.com/WebAssembly/wasi-tls/tree/6781ae26084100c0628ef72cc44e4517c6c48ae5/wit),
Jco's [local contract](https://github.com/bytecodealliance/jco/tree/main/packages/jco/lib/wit/builtin/wasi-tls-0.2.0-draft)
retains `wasi:tls@0.2.0-draft` but uses `wasi:io@0.2.12`, adds `is-available`, and
omits unstable-feature annotations. It is a provisional interface for Node.js,
web, and other host implementations. It exposes client handshake,
future polling, streams, and output shutdown. It has no server handshake,
certificate configuration, or ALPN controls. Only guest `servername` and
`rejectUnauthorized: true` are supported; other TLS options, including `ca`, are
rejected. TLS support is independent of the componentization backend.

> [!NOTE]
> `componentize-qjs` 0.4.3 currently fails during snapshot initialization when linking
> the TLS interface's shared IO resources, even for an otherwise empty component.
> StarlingMonkey is a workaround for this build-time issue.

The temporarily skipped component tests in `https-wasi-tls.ts` include deterministic
local TLS tests and a separately named public test requiring DNS and TCP/443 to
`example.com` (20-second execution deadline).

An `https.Server` always carries its `tls` record, even when no material was
supplied, so an implementation without a TLS stack refuses it; the `direct`
host then behaves like Node, which constructs the server and fails each
handshake. Because `jco:node/http@0.1.0` gained the record in place, a project
whose `wit/deps/jco-node-0.1.0/http.wit` predates it must delete that file so
the next `jco componentize` reinstalls the current interface: injection never
overwrites an existing dependency file.
TLS options are configured through `jco:node/tls@0.1.0`. Direct HTTP requests
and servers carry a one-use configuration handle, which the HTTP host consumes
from the same TLS provider. Certificate, cipher, ALPN, trust, and SNI settings
have one capability boundary instead of separate HTTP and TLS WIT records.
The existing serializable HTTPS option subset remains supported; native objects
and callback options such as `checkServerIdentity` and `SNICallback` remain
explicitly unsupported by the buffered HTTP adapter.

| Value | `node:https` behavior |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `direct` | Clients and servers using `jco:node/http` and `jco:node/tls`. Bind `createHttpHost(callbacks, tls)` to the same TLS provider imported by the component. |
| `wasi-sockets` | Verified clients call `jco:node/tls.start-tls` over the existing TCP streams. A provider can delegate to `wasi:tls`. The pinned draft does not support HTTPS servers. |
| `wasi-http` | HTTPS is rejected because outgoing-handler cannot use the TLS capability. Select `direct` or `wasi-sockets`. |

For WASI stream upgrades, bind `createWasiTlsBridge(yourWasiTlsProvider)` from
`@bytecodealliance/jco-std/wasi/0.2.x/node/24.x.x/tls/wasi` as the primary
`jco:node/tls` import. The bridge reuses the WASI TLS future, connection, and IO
resources. Only `servername` and `rejectUnauthorized: true` are supported by
that draft; trust and ALPN are provider policy. The optional native TLS factory
also accepts `{ wasiTls: yourWasiTlsProvider }` to serve both transports.
The existing native WASI TLS provider still awaits publication of the
preview2-shim `io-worker` export; the new native Node TLS provider does not
have that dependency.

Projects with checked-in `http.wit` or `http2.wit` dependencies must update them
together with `tls.wit`. Injection adds missing files but never overwrites
existing dependency files. Plain HTTP continues to work without granting TLS.
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ compatibility limits. Related submodules share their parent API page. See the
| [`node:fs`](./fs.md), [`node:fs/promises`](./fs.md) | Synchronous, callback, and promise facades over an explicit filesystem capability; denied by default. |
| [`node:http`](./http.md) | Client and server APIs over a selectable direct, Preview 2 sockets, or Preview 2 WASI HTTP implementation. Servers need `direct` or `wasi-sockets`. |
| [`node:http2`](./http2.md) | Client and server sessions over selectable direct or WASI socket implementations. |
| [`node:https`](./https.md) | The `node:http` core with the `https:` profile and a TLS-aware `Agent`; same implementation selection. TLS uses the `direct` host or an explicit `wasi:tls` provider. |
| [`node:https`](./https.md) | The `node:http` core with the `https:` profile and a TLS-aware `Agent`; same implementation selection. TLS uses `jco:node/tls`, optionally delegating to `wasi:tls`. |
| [`node:inspector`](./inspector.md), [`node:inspector/promises`](./inspector.md) | Session, console, and broadcast surface over an explicit host capability; denied by default. The host calls back through a guest-exported interface. |
| [`node:module`](./module.md) | Classification, source maps and `require.resolve` are exact. Everything that **loads** throws `ERR_JCO_UNSUPPORTED_NODE_API`. Requires no WIT capability. |
| [`node:net`](./net.md) | TCP clients, servers, and address utilities over Preview 2 `wasi:sockets`; native handles and IPC are unsupported. |
Expand All @@ -65,6 +65,7 @@ compatibility limits. Related submodules share their parent API page. See the
| [`node:string_decoder`](./string-decoder.md) | Guest-local streaming decoder for Node 24. Requires no WIT capability. |
| [`node:test`](./test.md), [`node:test/reporters`](./test.md) | Serial component tests, hooks, assertions, mocks, and reporters. No additional WIT imports. Runner requires engine `AbortController`; see the API page for engine limits. |
| [`node:timers`](./timers.md), [`node:timers/promises`](./timers.md) | Node 24 timer handles and promise timers over engine task scheduling; see the API page for runtime limits. |
| [`node:tls`](./tls.md) | Encrypted sockets, contexts, and inspection over `jco:node/tls`; denied by default. |
| [`node:tty`](./tty.md) | Node 24.20 `isatty`, `ReadStream` and `WriteStream` over the host process's descriptors through an explicit host capability; denied by default. |
| [`node:url`](./url.md) | Node 24 URL, URLSearchParams, URLPattern, domain and file conversions; relative file paths use optional WASI environment imports. |

Expand Down
10 changes: 5 additions & 5 deletions docs/src/interop/nodejs-builtins/supported-modules/process.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,10 @@ Generate bindings for explicit instantiation; no custom mapping is needed:
jco transpile app.wasm -o out --instantiation async
```

Then pass your implementation object directly in the imports. Jco's default
mapping names the process import after the denial-provider package; that key does
not force you to use its implementation. The generated binding types list the
expected import keys:
Then pass your implementation object directly in the imports, keyed by the WIT
interface name without its version. Jco's deny-by-default module map applies only
to ESM output; instantiation output never renames imports. The generated binding
types list the expected import keys:

```js
import { instantiate } from './out/app.js';
Expand All @@ -111,7 +111,7 @@ import { createProcessHost } from './my-process-provider.js';
const { host, exitRequests } = createProcessHost();
const component = await instantiate(undefined, {
...new WASIShim().getImportObject(),
'@bytecodealliance/jco-std/wasi/0.2.x/node/24.x.x/process/host': host,
'jco:node/process': host,
});
```

Expand Down
Loading
Loading