@bbk47/yamux
v0.1.0
Published
Production-hardened Yamux multiplexer for Node.js (TypeScript), interoperable with hashicorp/yamux. Fork of @llmcode/yamux-ts with the unknown-stream session-teardown fix.
Readme
@bbk47/yamux
Production-hardened TypeScript implementation of Yamux for Node.js, interoperable with hashicorp/yamux (Go).
This library multiplexes many logical Duplex streams over a single underlying transport (typically a TCP socket), following the Yamux framing and stream lifecycle rules.
Fork. This is a maintained fork of
@llmcode/yamux-ts(wangcode/yamux-ts). See Fork changes for what differs from upstream.
Features
- Yamux frame codec (12-byte header, big-endian)
SYN/ACK/FIN/RSTstream lifecycle- Per-stream flow control (default 256 KB)
- Session-level
PingandGoAway - Node.js
DuplexAPI for each logical stream - Interop tests with
hashicorp/yamux(Go) - Robust against late/duplicate frames for closed streams (does not tear down the whole session)
Fork changes
Relative to @llmcode/[email protected]:
- Unknown-stream frames no longer kill the session. Previously a late or duplicate frame
(e.g. a trailing
WindowUpdate/FINfor an already-closed stream) threw a fatalYamuxProtocolError, which propagated toGoAway(ProtocolError)+close()and destroyed the entire session and all its streams. This reliably broke any yamux server that opens one stream per inbound connection (the 2nd connection died). It now mirrorshashicorp/yamux: ignore the frame, and replyRSTfor non-teardown frames so the peer stops. - Duplicate
SYNfor a known stream resets only that stream instead of tearing down the session. - Regression tests added in
test/unit/session.test.tscovering both cases.
Install
npm install @bbk47/yamux
# or: pnpm add @bbk47/yamuxQuick Start
Client side
import net from "node:net";
import { Client } from "@bbk47/yamux";
const socket = net.connect(9000, "127.0.0.1");
socket.once("connect", async () => {
const session = Client(socket);
session.on("error", (err) => {
console.error("session error", err);
});
const stream = session.openStream();
stream.write("hello over yamux\n");
stream.end();
for await (const chunk of stream) {
process.stdout.write(chunk);
}
const ping = await session.ping();
console.log("rtt(ms)", ping.rttMs);
session.goAway();
session.close();
});Server side
import net from "node:net";
import { Server } from "@bbk47/yamux";
const server = net.createServer((socket) => {
const session = Server(socket);
session.on("stream", (stream) => {
stream.on("data", (chunk) => {
// Echo back.
stream.write(chunk);
});
stream.on("end", () => {
stream.end();
});
});
session.on("goaway", (code) => {
console.log("peer sent goaway", code);
});
session.on("error", (err) => {
console.error("session error", err);
session.close();
});
});
server.listen(9000, "127.0.0.1");API
Client(transport, config?)
Create a Yamux session in client mode (outbound stream IDs are odd: 1, 3, 5...).
Server(transport, config?)
Create a Yamux session in server mode (outbound stream IDs are even: 2, 4, 6...).
createClientSession(transport, options?) and createServerSession(transport, options?)
Compatibility aliases for users who prefer explicit factory names. They are functionally equivalent to Client and Server.
new YamuxSession(transport, options)
options / config:
role: "client" | "server"(required)initialStreamWindow?: numberdefault256 * 1024maxFrameSize?: numberdefault64 * 1024
Methods:
openStream(): YamuxStreamping(timeoutMs?: number): Promise<{ nonce: number; rttMs: number }>goAway(code?: GoAwayCode): voidclose(): void
Events:
streamincomingYamuxStreamgoawaypeer session termination codeerrorsession/protocol errorclosesession closed
YamuxStream (extends Duplex)
Use it as a normal Node stream:
- write with
stream.write()/stream.end() - read with
stream.on("data")/ async iteration - remote half-close maps to
end - reset maps to stream error (
YamuxStreamResetError)
Constants and Types
Exported protocol constants:
YAMUX_VERSIONHEADER_SIZEFrameTypeFrameFlagGoAwayCodeDEFAULT_INITIAL_WINDOWDEFAULT_MAX_FRAME_SIZE
Exported low-level helpers:
encodeFrame,decodeFrame,decodeHeader,writeHeaderFrameParser,YamuxCodec
Error Semantics
YamuxProtocolError: invalid frame or protocol violationYamuxClosedError: operation on closed/goaway sessionYamuxStreamResetError: stream reset (RST)
On a genuine protocol violation (e.g. a malformed frame), the session sends GoAway(ProtocolError) and closes. Late/duplicate frames for unknown (already-closed) streams are not treated as violations — they are tolerated and answered with RST, matching hashicorp/yamux.
Development
pnpm typecheck
pnpm test
pnpm test:interop
pnpm buildInterop Testing
pnpm test:interop starts a Go process in test/interop/go using hashicorp/yamux and validates:
- stream open + payload echo
- concurrent streams
- ping roundtrip
LLM Integration Guide
For code generation agents, see:
docs/LLM_GUIDE.md
