@rstreamlabs/runtime
v0.8.3
Published
Node.js runtime SDK for the rstream tunnel protocol.
Downloads
314
Readme
@rstreamlabs/runtime
Node.js runtime SDK for the rstream tunnel protocol.
Use this package when a Node.js process needs to create bytestream tunnels, accept incoming tunnel streams, dial private bytestream tunnels, or attach a tunnel to a Node HTTP server. This package is intentionally Node-only: it uses TCP, TLS, long-lived sockets, and Node stream primitives.
For Engine API inventory, events, webhooks, token minting, and TURN helpers, use
@rstreamlabs/tunnels. For Control plane APIs, use
@rstreamlabs/rstream.
Install
npm install @rstreamlabs/runtimeHTTP tunnel server
import http from "node:http";
import os from "node:os";
import { Client } from "@rstreamlabs/runtime";
const client = Client.fromEnv();
const ctrl = await client.connect();
const tunnel = await ctrl.createTunnel({
protocol: "http",
httpVersion: "http/1.1",
publish: true,
});
const server = http.createServer((_req, res) => {
res.writeHead(200, { "content-type": "text/plain" });
res.end(os.hostname());
});
console.log("Server accessible at:", await tunnel.forwardingAddress());
await tunnel.serve(server);serve() feeds accepted tunnel streams into the regular Node HTTP server
connection path. Existing request, upgrade, timeout, and server lifecycle
handlers continue to work as they do on a local TCP listener. serveHttp() is
also available when a call site benefits from the more explicit name.
Set mtlsAuth: true to require a registered client certificate for requests
entering the published tunnel URL. This is separate from agent authentication:
the Node process can authenticate to the engine with a token or with
RSTREAM_MTLS_CERT_FILE / RSTREAM_MTLS_KEY_FILE, while mtlsAuth protects
downstream clients reaching the public tunnel endpoint.
Published TCP tunnel
Create a raw TCP bytestream with protocol: "tcp". Leave port unset for an ephemeral public address, or set it to a port already reserved by the project through the Control plane:
const tunnel = await ctrl.createTunnel({
protocol: "tcp",
publish: true,
port: 10042,
});
console.log(await tunnel.forwardingAddress());The runtime can accept the resulting streams or forward them with normal Node stream APIs. It does not reserve the port itself. Published TCP does not add downstream encryption or authentication; use a secure application protocol such as SSH, or choose a TLS tunnel for TLS traffic.
WebSocket upgrades
WebSocket works over an HTTP/1.1 bytestream tunnel because the SDK passes the
raw stream to Node's HTTP server. Handle upgrade on the server or attach a
WebSocket library that integrates with http.Server.
import http from "node:http";
import { WebSocketServer } from "ws";
import { Client } from "@rstreamlabs/runtime";
const ctrl = await Client.fromEnv().connect();
const tunnel = await ctrl.createTunnel({
protocol: "http",
httpVersion: "http/1.1",
publish: true,
});
const server = http.createServer();
const wss = new WebSocketServer({ noServer: true });
server.on("upgrade", (req, socket, head) => {
wss.handleUpgrade(req, socket, head, (ws) => {
ws.send("connected through rstream");
});
});
console.log(await tunnel.forwardingAddress());
await tunnel.serve(server);Dial a private bytestream tunnel
import { Client } from "@rstreamlabs/runtime";
const client = Client.fromEnv();
const stream = await client.dial("private-api");
stream.write("ping\n");
stream.on("data", (chunk) => {
process.stdout.write(chunk);
});The returned object is a regular Node Duplex stream backed by the rstream
bytestream connection. You can pipe it, wrap it with tls.connect({ socket }),
or adapt it to protocol clients that accept a custom stream.
Control liveness and established streams
The runtime negotiates a bounded heartbeat policy with the engine. A missed
heartbeat deadline closes the control channel, rejects pending tunnel accepts,
and prevents new streams from being admitted through that tunnel. A Duplex
already returned by accept() is an independent payload connection: it remains
usable in both directions and can drain normally after an unexpected control
loss. This keeps a transient or asymmetric control-path outage from interrupting
an established application session while still withdrawing stale admissions.
An explicit tunnel or control close remains a hard lifecycle operation. The
engine revokes the tunnel and terminates its payload connections; applications
must therefore use the returned stream events as the final payload-lifecycle
signal rather than infer payload closure from ctrl.done() alone.
Configuration
The SDK supports the same configuration model as the Go and C++ SDKs:
| Variable | Purpose |
| ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| RSTREAM_CONFIG | Path to the rstream configuration file. Defaults to ~/.rstream/config.yaml. |
| RSTREAM_CONTEXT | Name of the context to select during config-based resolution. |
| RSTREAM_API_URL | Control plane API URL used by managed project resolution. |
| RSTREAM_REGION | Authorized region to select for a managed project. |
| RSTREAM_CONTROL_PLANE_HEADERS | Additional Control plane request headers encoded as a JSON object. |
| RSTREAM_ENGINE | Engine endpoint used when no engine is provided explicitly. |
| RSTREAM_AUTHENTICATION_TOKEN | Bearer token for token-authenticated runtime connections. |
| RSTREAM_MTLS_CERT_FILE | Client certificate file for mTLS agent authentication. |
| RSTREAM_MTLS_KEY_FILE | Client private key file for mTLS agent authentication. |
| RSTREAM_TUNNEL_TRANSPORT | auto, tls, or quic. auto uses TLS; explicit quic fails because this runtime has no QUIC implementation. |
| RSTREAM_QUIC_TRANSPORT | Legacy selector. 1 requests QUIC and therefore fails in this runtime; prefer RSTREAM_TUNNEL_TRANSPORT. |
| HTTPS_PROXY / HTTP_PROXY / ALL_PROXY / NO_PROXY | Used only when transport.proxy.fromEnvironment: true is set. HTTP and HTTPS proxies are supported for the TLS runtime transport. |
Explicit SDK options take precedence over environment and config-file values.
The equivalent client option is tunnelTransport. In shared YAML, use
transport.mode. Omitting the setting or choosing auto selects TLS because
the Node runtime currently implements only TLS. This fallback is a documented
capability rule; an explicit quic request is never silently downgraded.
The runtime reads process proxy variables, not desktop proxy preference stores
such as macOS System Settings or Windows Internet Options.
For HTTPS proxies issued by a private CA, use transport.proxy.tls.caFile in
the shared rstream config. transport.proxy.tls.serverName overrides the proxy
SNI and certificate verification name, and
transport.proxy.tls.insecureSkipVerify disables HTTPS proxy verification for
diagnostics. The proxy TLS settings apply to the proxy hop only; the runtime
still verifies the rstream engine TLS session separately after CONNECT succeeds.
Token authentication and mTLS engine authentication are mutually exclusive. When
noToken: true is set, the SDK does not read or send stored tokens.
Region selection requires a managed project endpoint and cannot be combined
with an explicit engine. Additional Control plane headers are for deployment
access layers; authentication, forwarding, and hop-by-hop headers are reserved.
Malformed values and case-insensitive duplicates are rejected before network
I/O.
import { Client } from "@rstreamlabs/runtime";
const client = new Client({
engine: "engine.example.com:443",
token: process.env.RSTREAM_AUTHENTICATION_TOKEN,
});Managed project resolution is also available:
import { Client } from "@rstreamlabs/runtime";
const client = new Client({
credentials: {
clientId: process.env.RSTREAM_CLIENT_ID!,
clientSecret: process.env.RSTREAM_CLIENT_SECRET!,
},
projectEndpoint: "project-endpoint",
});Supported runtime surface
Supported:
- control-channel TLS transport with ALPN
rstrm/1 - HTTP CONNECT proxying for the control-channel TLS transport through
transport.proxy.http - HTTPS proxy verification through
transport.proxy.tls.caFile,transport.proxy.tls.serverName, andtransport.proxy.tls.insecureSkipVerify - opt-in process proxy discovery through
transport.proxy.fromEnvironment - bytestream tunnel creation and close
- published TCP creation with ephemeral or reserved project ports
- inbound bytestream accept
- private bytestream dial
- HTTP/1.1 and h2c upstream integration through Node HTTP servers
- WebSocket upgrades over HTTP/1.1 tunnels
- config-file and environment resolution compatible with the Go/C++ SDK model
Not supported:
- datagram tunnels
- QUIC transport
- SOCKS5 proxying
- HTTP/3 / WebTransport tunnels
- browser runtimes
- custom DNS resolver settings from config files
Unsupported features fail during configuration or tunnel creation with explicit
RuntimeError.code values.
Tests
The package includes a local TLS/protobuf engine harness covering:
- control-channel handshake and close
- negotiated heartbeat recovery, timeout, and established-stream draining
- HTTP CONNECT proxying for the control-channel TLS transport
- mTLS control-channel authentication
- bytestream tunnel create/close
- inbound proxy connection accept
- private dial
- HTTP serving
- WebSocket upgrade routing
- engine error propagation
- config precedence and auth conflict checks
Run:
npm --workspace @rstreamlabs/runtime run testFor a real engine integration check, start an engine first and provide the same runtime configuration used by the CLI:
RSTREAM_ENGINE=localhost:9443 \
RSTREAM_AUTHENTICATION_TOKEN=... \
RSTREAM_RUNTIME_E2E_TLS_INSECURE=1 \
npm --workspace @rstreamlabs/runtime run test:e2eFor mTLS agent authentication:
RSTREAM_ENGINE=localhost:9443 \
RSTREAM_MTLS_CERT_FILE=/etc/rstream/client.pem \
RSTREAM_MTLS_KEY_FILE=/etc/rstream/client-key.pem \
RSTREAM_RUNTIME_E2E_TLS_INSECURE=1 \
npm --workspace @rstreamlabs/runtime run test:e2eThe real-engine E2E script verifies:
- private bytestream creation
- direct private dial by tunnel name and by tunnel ID
- bidirectional byte relay on both private streams
- published HTTP tunnel creation
- HTTPS fetch through the published forwarding URL
- WebSocket upgrade through the published forwarding URL
- mTLS agent authentication when certificate/key variables are configured
- published
mtlsAuthrejection without a client certificate - published
mtlsAuthacceptance with the registered client certificate - published
mtlsAuthrejection when mTLS is combined withrstream_auth
RSTREAM_RUNTIME_E2E_TLS_INSECURE=1 is intended for local engines using
development certificates.
Development
npm --workspace @rstreamlabs/runtime run test
npm --workspace @rstreamlabs/runtime run test:e2e
npm --workspace @rstreamlabs/runtime run type-check
npm --workspace @rstreamlabs/runtime run lint
npm --workspace @rstreamlabs/runtime run build