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

proxinator

v0.0.3

Published

Proxinator is proxy swiss-army knife

Downloads

293

Readme

Proxinator

npm version license tests

Composable Node.js proxy toolkit with zero-copy TCP forwarding. Chain forward (HTTP CONNECT), reverse (SNI), and SOCKS5 proxies in a few lines — traffic flows through the kernel, not your code.

Installation

yarn add proxinator

Quick Start

const proxinator = require("proxinator");

const proxy = proxinator.server.forward();

proxy.on("connection", (connection) => {
	proxinator.client.reverse(connection.getDestination()).then((socket) => {
		connection.bind(socket);
	});
});

proxy.http.listen(8080);

Features

  • Servers — Forward (HTTP CONNECT), Reverse (SNI), SOCKS5
  • Clients — Forward, SOCKS5, Reverse (direct), Chaining, http.Agent
  • Utilities — SNI parser, DNS randomization + caching + bogus response filtering, load balancer, measurement streams, hostname obfuscation

Usage

Servers

Forward Proxy Server

Accept HTTP CONNECT requests and tunnel connections:

const proxinator = require("proxinator");

const proxy = proxinator.server.forward();

proxy.on("connection", (connection) => {
	const auth = connection.getAuth();
	const destination = connection.getDestination();

	console.log("Connect to:", destination.hostname + ":" + destination.port);

	proxinator.client.reverse(destination).then((socket) => {
		connection.bind(socket);
	});
});

proxy.http.listen(8080);

Reverse Proxy Server

Accept TCP connections and route by SNI domain:

const proxinator = require("proxinator");

const proxy = proxinator.server.reverse();

proxy.on("connection", (connection) => {
	connection.waitSNI().then((hostname) => {
		console.log("SNI hostname:", hostname);

		const target = new URL("tcp://" + hostname + ":443");

		return proxinator.client.reverse(target).then((socket) => {
			connection.bind(socket);
		});
	});
});

proxy.tcp.listen(443);

SOCKS5 Proxy Server

Accept SOCKS5 CONNECT requests and tunnel connections:

const proxinator = require("proxinator");

const proxy = proxinator.server.socks5();

proxy.on("connection", (connection) => {
	const auth = connection.getAuth();
	const destination = connection.getDestination();

	console.log("SOCKS5 connect to:", destination.hostname + ":" + destination.port);

	proxinator.client.reverse(destination).then((socket) => {
		connection.bind(socket);
	});
});

proxy.tcp.listen(1080);

With protocol-level authentication — credentials are validated during the SOCKS5 handshake, before the CONNECT request. No-auth clients are also checked (with username/password undefined). Socket info is always included:

const proxy = proxinator.server.socks5(null, {
	authenticate: (info) => {
		// info: { username, password, remoteAddress, remotePort, localAddress, localPort }
		// username/password are undefined for no-auth clients
		return info.username === "admin" && info.password === "secret";
	}
});

Clients

Forward Proxy Client

Connect through an upstream proxy:

const proxinator = require("proxinator");

const proxy = new URL("http://user:[email protected]:8080");
const target = new URL("tcp://example.com:443");

proxinator.client.forward(proxy, target).then((socket) => {
	console.log("Tunneled connection established");
});

SOCKS5 Proxy Client

Connect through an upstream SOCKS5 proxy:

const proxinator = require("proxinator");

const proxy = new URL("socks5://user:[email protected]:1080");
const target = new URL("tcp://example.com:443");

proxinator.client.socks5(proxy, target).then((socket) => {
	console.log("SOCKS5 tunnel established");
});

Pre-existing Sockets

Clients can also accept a pre-existing socket for manual chaining:

// Send CONNECT through an existing socket
proxinator.client.forward(proxy, target, { socket: existingSocket });

// Send SOCKS handshake through an existing socket
proxinator.client.socks5(proxy, target, { socket: existingSocket });

// Return an existing socket directly (passthrough)
proxinator.client.reverse(target, { socket: existingSocket });

Chaining & Agent

Proxy Chaining

Chain connections through multiple proxies. Each proxy becomes a hop through the previous tunnel. Supports mixing HTTP, HTTPS, and SOCKS5 proxies:

const proxinator = require("proxinator");

const proxyA = new URL("http://user:[email protected]:8080");
const proxyB = new URL("socks5://proxy-b.example.com:1080");
const target = new URL("tcp://example.com:443");

// Multi-hop: client → HTTP proxy → SOCKS5 proxy → target
proxinator.client.chain([proxyA, proxyB], target).then((socket) => {
	console.log("Chained connection established");
});

// Single proxy (same as forward/socks5)
proxinator.client.chain([proxyA], target).then((socket) => {
	console.log("Single hop");
});

// No proxies (falls back to direct connection)
proxinator.client.chain([], target).then((socket) => {
	console.log("Direct connection");
});

// With custom DNS (only used for the first hop)
const lookup = proxinator.utils.dnsLookup();

proxinator.client.chain([proxyA, proxyB], target, { lookup });

http.Agent

Use proxy chains as a drop-in agent for http.request and https.request:

const proxinator = require("proxinator");
const http = require("http");

const agent = proxinator.client.agent([
	new URL("http://proxy-a.example.com:8080"),
	new URL("socks5://proxy-b.example.com:1080")
]);

http.get("http://example.com", { agent }, (res) => {
	res.pipe(process.stdout);
});

Split IP + SNI (Agent Connect Transform)

Use the agent's connect option to obfuscate the CONNECT target while the real hostname is used as TLS SNI. The proxy only sees the obfuscated address, but the TLS handshake inside the tunnel uses the original hostname:

const proxinator = require("proxinator");
const https = require("https");
const nipio = require("proxinator").utils.obfuscator.nipio;

const agent = proxinator.client.agent([
	new URL("http://proxy.example.com:8080")
], {
	connect: (host, port) => {
		return nipio.convert(host).then((obfuscated) => {
			return new URL("tcp://" + obfuscated + ":" + port);
		});
	}
});

// Proxy sees CONNECT to "c0a80101.nip.io:443"
// TLS SNI is "example.com" (set automatically by Node's https module)
https.get("https://example.com", { agent }, (res) => {
	res.pipe(process.stdout);
});

Routing

Advanced Routing

Route specific domains directly while proxying everything else through an upstream proxy:

const proxinator = require("proxinator");

const PROXY_URL = new URL("http://user:[email protected]:8080");
const DIRECT_DOMAINS = ["cdninstagram.com", "static.xx.fbcdn.net"];

const server = proxinator.server.forward();

server.on("connection", (connection) => {
	const destination = connection.getDestination();
	const domain = destination.hostname;

	if(DIRECT_DOMAINS.indexOf(domain) !== -1) {
		return proxinator.client.reverse(destination).then(socket => {
			connection.bind(socket);
		});
	}

	proxinator.client.forward(PROXY_URL, destination).then(socket => {
		connection.bind(socket);
	});
});

server.http.listen(8080);

Utilities

SNI Parser

Extract hostname from TLS ClientHello:

const sni = require("proxinator").utils.sni;

// Check if buffer is a TLS ClientHello
sni.isClientHello(buffer); // true/false

// Extract SNI hostname
sni.parseSNI(buffer); // "example.com" or null

Load Balancer

Weighted random selection:

const createPool = require("proxinator").utils.balancer;

const pool = createPool();

pool.push("server-a", 3); // weight 3
pool.push("server-b", 1); // weight 1

pool.getRandomCandidate(); // "server-a" ~75%, "server-b" ~25%

Measurement Streams

Chain speed and bandwidth measurement into a single transform, then plug it into bind() to monitor traffic:

const proxinator = require("proxinator");

const server = proxinator.server.forward();

server.on("connection", (connection) => {
	const destination = connection.getDestination();

	// Chain multiple measurements into one transform
	const meter = proxinator.measure.chain([
		proxinator.measure.bandwidth,
		proxinator.measure.speed
	]);

	// Events from inner streams are forwarded to the chain
	meter.on("bandwidth", (total) => {
		console.log(destination.hostname, "total:", total, "bytes");
	});

	meter.on("speed", (bps) => {
		console.log(destination.hostname, "speed:", Math.round(bps), "bytes/sec");
	});

	proxinator.client.reverse(destination).then((socket) => {
		// Pass as up transform to measure server-to-client traffic
		connection.bind(socket, meter);
	});
});

server.http.listen(8080);

DNS Configuration

By default, clients use system DNS. You can optionally set up random DNS resolution and caching:

const proxinator = require("proxinator");

// System DNS (default — no setup needed)
proxinator.client.reverse(target);
proxinator.client.forward(proxy, target);

// Quick setup — random DNS + cache in one call (create once, reuse across connections)
const lookup = proxinator.utils.dnsLookup();

proxinator.client.reverse(target, { lookup });
proxinator.client.forward(proxy, target, { lookup });

// Custom DNS servers
const customLookup = proxinator.utils.dnsLookup(["8.8.8.8", "1.1.1.1"]);

proxinator.client.reverse(target, { lookup: customLookup });

// Manual setup — resolver and cache separately
const resolver = proxinator.utils.dns();
const cache = proxinator.utils.dnsCache(resolver);

proxinator.client.reverse(target, { lookup: cache.lookup });

// Cache only (system DNS with caching, no random resolver)
const systemCache = proxinator.utils.dnsCache();

proxinator.client.reverse(target, { lookup: systemCache.lookup });

// Bogus IP filtering (enabled by default — retries when DNS returns loopback, private, etc.)
// Customize which ranges are considered bogus:
const filteredLookup = proxinator.utils.dnsLookup(null, {
	bogusRanges: ["loopback", "private"],  // only filter these ranges
	maxBogusRetries: 5                      // retry up to 5 times (default: 3)
});

// Disable bogus filtering entirely:
const unfilteredLookup = proxinator.utils.dnsLookup(null, { bogusRanges: [] });

// IPv4 only — restrict DNS resolution to A records
proxinator.client.reverse(target, { lookup, family: 4 });
proxinator.client.forward(proxy, target, { lookup, family: 4 });
proxinator.client.chain([proxyA], target, { lookup, family: 4 });

// IPv6 only
proxinator.client.reverse(target, { family: 6 });

// Via agent
const ipv4Agent = proxinator.client.agent([proxyA], { lookup, family: 4 });

Hostname Obfuscation

Convert hostnames or IPs to nip.io format to bypass proxy provider restrictions:

const nipio = require("proxinator").utils.obfuscator.nipio;

// From an IPv4 address (hex-encoded to avoid regex detection)
nipio.fromIP("10.0.0.1"); // "0a000001.nip.io"

// Auto-detect IP vs hostname
nipio.convert("192.168.1.1");   // Promise<"c0a80101.nip.io">
nipio.convert("example.com");   // Promise<"5db8d822.nip.io">

Events

Connection Events

All servers (forward, reverse, SOCKS5) emit events for monitoring and logging:

const proxinator = require("proxinator");

const server = proxinator.server.forward();

server.on("connection", (connection) => {
	console.log("New connection from:", connection.getRemoteHost());
	console.log("Active connections:", server.connectionCount());

	proxinator.client.reverse(connection.getDestination()).then((socket) => {
		connection.bind(socket);
	});
});

server.on("close", (connection) => {
	console.log("Connection closed, remaining:", server.connectionCount());
});

server.on("error", (error, connection) => {
	console.error("Error:", error.message);
});

server.http.listen(8080);

Available events:

  • "connection" — new connection accepted
  • "close" — connection socket closed
  • "error" — socket error during bind or SNI parsing

How It Works

Proxinator is a thin routing layer. Once a connection is established and bind() is called, Node.js takes over entirely — socket.pipe(socket) delegates data transfer to libuv, which uses kernel-level buffering. Traffic flows through the OS networking stack without being copied into JavaScript heap memory.

This means:

  • No per-byte JS overhead — data moves through native buffers, not JS strings or objects
  • No parsing — unlike HTTP proxies that inspect/rewrite traffic, Proxinator operates at the TCP level and forwards raw bytes
  • Measurement is opt-in — the measure transforms only add overhead when you explicitly chain them into bind()
  • JS only handles routing decisions — auth checks, SNI parsing, and destination selection happen once per connection, then the native layer handles the sustained data transfer

The result is that sustained throughput is limited by the OS and network, not by Node.js.

Planned Features

  • Proxy checker — validate proxy liveness, latency, and anonymity
  • TLS stripping — connect to upstream, extract cert parameters, generate matching cert, and bind decrypted sockets for inspection

License

GPL-2.0