npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@coveragemap/speed-transport

v0.2.0

Published

Raw TCP, WebSocket, and secure WebSocket on one port, with minimal CPU per byte, for the CoverageMap speed test protocol

Readme

@coveragemap/speed-transport

npm License: Apache 2.0 Node.js

Raw TCP, WebSocket, and secure WebSocket on one port, built for moving bytes with as little CPU as possible. It powers the CoverageMap speed test servers and the raw TCP transport of @coveragemap/speed-test.

tcp://host:port        raw TCP
ws://host:port/v1/ws   WebSocket
wss://host:port/v1/ws  secure WebSocket
https://host:port/...  your HTTP routes

All of them share one listening port. The server reads the first bytes of each connection to decide what it is. A TLS ClientHello is decrypted and handed to HTTP, the 7 byte preamble STCP/1\n starts a raw TCP session, and anything else is HTTP. No VM, kernel, or proxy changes are needed: deploying the app is enough.

Raw TCP is always plaintext. Speed test traffic carries no private data, so TLS is only used where browsers need it: secure WebSocket from HTTPS pages.

Why

A speed test server mostly receives bytes it ignores and sends zeros. General purpose WebSocket servers unmask, copy, and buffer every upload byte and build an object per message, so at multi-gigabit speeds they spend most of their CPU on work the test throws away. This library:

  • Counts upload messages without reading them. Messages above a threshold (64 KiB by default, 128 bytes in the speed test server) are never unmasked, copied, or buffered: the parser reads the frame header and skips the payload.
  • Sends from shared buffers. Download frames reuse one zero buffer and a cached header, written with cork/uncork, and pause on the socket's drain event.
  • Offers raw TCP. The same frames without masking or an HTTP handshake. A Node.js client then never masks uploads and never copies downloads either.
  • Spreads across cores. A primary process accepts connections without reading them and hands each one to a worker process, with per-client connection limits, certificate updates, and broadcasts shared across workers.

On loopback one worker process serves 26 Gbps of raw TCP download, and upload needs 10 to 30% less server CPU per Gbps than the ws server it replaced, with raw TCP needing a quarter of the client CPU. Secure WebSocket download is bound by TLS and costs the same. See benchmarks for the numbers and why they matter.

It has no runtime dependencies and needs Node.js 20 or later. It passes the Autobahn WebSocket conformance suite (301 cases; compression is never negotiated, so 12.* and 13.* do not apply).

What it is for

  • Speed test servers and clients. The CoverageMap speed test protocol is built in (createSpeedTestServer), and the raw TCP client is what @coveragemap/speed-test uses in Node.js.
  • Moving bulk data where the payload is counted, not read. Throughput tests, load generators, and network diagnostics that need multi-gigabit rates with little CPU.
  • Serving different clients on one port. Browsers connect over secure WebSocket, Node.js clients over raw TCP, and HTTP routes such as health checks sit next to them, all without extra ports, proxies, or firewall rules.
  • Using every core of a server without infrastructure changes. The cluster is plain Node.js processes, so a normal deploy is enough.
  • Small custom message protocols that want the same properties: createSpeedTransportServer hands you each session's messages and leaves the protocol to you.

What it is not for

  • General purpose WebSocket applications such as chat, live dashboards, or multiplayer games. There is no compression, no subprotocol or extension negotiation, no rooms or broadcasting to clients, and no reconnection. Messages above maxBufferedMessageBytes arrive as a length only, never as data. Use ws or a framework built on it instead.
  • Browsers. The raw TCP client needs Node.js (node:net). Browsers use their built-in WebSocket against the same server.
  • Cloudflare Workers, Deno Deploy, and other serverless runtimes. The server needs Node.js sockets and child processes. Workers only accept HTTP, so they can serve the WebSocket side of the protocol with their own code, but never raw TCP.
  • Raw TCP through HTTP proxies, CDNs, or layer 7 load balancers. These only forward HTTP. Raw TCP needs a direct connection or a TCP (layer 4) pass-through; behind anything else, set tcp: false and let clients use WebSocket.
  • A full web server. HTTP on the same port is HTTP/1.1 only, meant for a few routes like /v1/server and health checks. HTTP/2 and HTTP/3 are not supported.
  • Authentication or encryption beyond TLS. It offers an authorize hook and per-client connection limits; anything else is up to the application.
  • A stable API yet. Until 1.0, minor versions may change the API. Pin the minor version.

Install

npm install @coveragemap/speed-transport

Quick start

A speed test server

import { createSpeedTestServer } from '@coveragemap/speed-transport';
import { readFileSync } from 'node:fs';

const server = createSpeedTestServer({
  tls: { cert: readFileSync('cert.pem'), key: readFileSync('key.pem') },
  websocket: { path: '/v1/ws' },
  requestListener: (req, res) => res.end('ok'), // or an Express app
});

await server.listen(443);

This answers the CoverageMap speed test protocol on all three transports: PING → PONG, START <kb> <count> → count zero frames, an ACK for every upload message over 128 bytes, and CLOSE → connection reset.

Your own protocol

import { createSpeedTransportServer } from '@coveragemap/speed-transport';

const server = createSpeedTransportServer({
  onConnection(connection) {
    connection.on('message', (data, binary) => {
      if (!binary && data.toString() === 'hello') connection.sendText('world');
    });
    connection.on('bulk', (length) => {
      // A large message, counted but never copied.
    });
  },
});

await server.listen(8080);

A raw TCP client

import { SpeedTransportSocket, probeTcpTransport } from '@coveragemap/speed-transport/client';

if (await probeTcpTransport('speed.example.com', 443)) {
  const socket = new SpeedTransportSocket('tcp://speed.example.com:443', { binaryPayloads: 'discard' });
  socket.binaryType = 'arraybuffer';
  socket.onopen = () => socket.send('PING');
  socket.onmessage = (event) => console.log(event.data); // 'PONG'
}

SpeedTransportSocket has the WebSocket API (send, close, readyState, bufferedAmount, binaryType, on* handlers, and EventTarget), so code written for WebSocket runs on it unchanged.

Several processes

// primary.js
import { createClusterPrimary } from '@coveragemap/speed-transport';

const primary = createClusterPrimary({
  workers: 4,
  workerModule: new URL('./worker.js', import.meta.url),
  port: 443,
  maxConnectionsPerClient: 32,
  secureContext: { cert, key },
});
await primary.start();

// worker.js
import { createSpeedTestServer, runClusterWorker } from '@coveragemap/speed-transport';

runClusterWorker((context) =>
  createSpeedTestServer({ tls: {}, limiter: context.limiter, websocket: { path: '/v1/ws' } })
);

See cluster.

Documentation

| Document | Contents | |---|---| | Wire protocol | Protocol detection, the raw TCP transport, framing rules, close behavior, and the speed test commands | | API | Every export of @coveragemap/speed-transport and @coveragemap/speed-transport/client | | Cluster | The primary and worker processes, limits across workers, certificates, and IPC | | Benchmarks | Throughput and CPU against ws, on every transport, and why it matters |

Cloudflare Workers and browsers

Browsers can only open WebSocket connections, and Cloudflare Workers only accept HTTP. Both keep working against this server over wss://, and clients choose raw TCP only where it exists: CoverageMap servers list their transports (WSSv1, WSv1, TCPv1) in a protocols field, and the CoverageMap client library uses raw TCP in Node.js only when it is listed, falling back to WebSocket if the first connection fails. Behind a reverse proxy, pass tcp: false (a proxy only forwards HTTP) and tls: null.

Development

npm install
npm run lint            # ESLint
npm run typecheck       # TypeScript, no emit
npm test                # Vitest: unit, integration, robustness, and cluster tests
npm run test:coverage   # The same with coverage thresholds
npm run autobahn        # Autobahn conformance suite in Docker
npm run bench           # Loopback benchmark against the ws baseline

The tests run real servers and clients over loopback: every transport, TLS, the cluster with real processes, malformed and hostile input, backpressure, and limits. CI runs them on Linux, Windows, and macOS with Node.js 20, 22, and 24, plus coverage, Autobahn, and a packaging check.

Releases are published to npm from the main branch by the manual Publish Package to npm workflow, with provenance, through npm trusted publishing (the package's trusted publisher must name this repository, publish-npm.yml, and the Production environment). Bump version and the changelog first.

License

Apache-2.0