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

wireguard-proxy

v0.1.4

Published

A Node.js/Bun library for proxying TCP, UDP, and HTTP traffic through WireGuard tunnels — entirely in userspace, no root or TUN device required.

Downloads

307

Readme

wireguard-proxy

A Node.js/Bun library for proxying TCP, UDP, and HTTP traffic through WireGuard tunnels — entirely in userspace, no root or TUN device required.

Features

  • Userspace WireGuard — No system-level VPN setup, no root privileges
  • Multiple concurrent tunnels — Route traffic through different exits simultaneously
  • TCP streamsReadableStream/WritableStream interface with backpressure
  • UDP sockets — Send/receive datagrams with remote address metadata
  • HTTP fetchproxy.fetch() works like the standard Fetch API, routed through a tunnel
  • Undici integration — Custom dispatcher for Node.js HTTP clients
  • Cross-runtime — Works with both Node.js and Bun via Node-API
  • Explicit routing — Select a tunnel per-request, or set a default

How It Works

JS/TS API → Native Addon (Rust) → boringtun (WireGuard) → smoltcp (TCP/IP) → Encrypted UDP → Peer

The native layer uses boringtun for the WireGuard protocol and smoltcp as a userspace TCP/IP stack. All packet processing happens inside the application — no kernel networking changes.

Installation

bun install wireguard-proxy
# or
npm install wireguard-proxy

You also need the native addon built for your platform. To build from source:

# Requires Rust toolchain
cd native && cargo build --release
cp target/release/libwireguard_proxy_native.so wireguard-proxy-native.node  # Linux
cp target/release/libwireguard_proxy_native.dylib wireguard-proxy-native.node  # macOS

Quick Start

import { createWireGuardProxy } from "wireguard-proxy"

const proxy = await createWireGuardProxy({ defaultTunnel: "main" })

await proxy.addTunnel({
  name: "main",
  config: `
[Interface]
PrivateKey = <your-private-key>
Address = 10.0.0.2/32
DNS = 1.1.1.1

[Peer]
PublicKey = <peer-public-key>
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = 203.0.113.10:51820
PersistentKeepalive = 25
`,
})

// Fetch a URL through the tunnel
const res = await proxy.fetch("https://api.ipify.org?format=json")
console.log(await res.text()) // Your tunnel exit IP

await proxy.close()

API

createWireGuardProxy(options?)

Creates a new proxy instance.

const proxy = await createWireGuardProxy({
  defaultTunnel: "main",   // Default tunnel for requests without explicit selection
  dns: "system",           // "system" | "tunnel" | "custom"
  resolve: async (host) => ["1.2.3.4"],  // Custom resolver (when dns: "custom")
})

proxy.addTunnel(options)

Add and connect a WireGuard tunnel. Accepts INI config text or a structured object.

const { id, name } = await proxy.addTunnel({
  name: "us-east",
  config: configText,  // string or WireGuardConfig object
})

proxy.removeTunnel(idOrName)

Disconnect and remove a tunnel.

await proxy.removeTunnel("us-east")

proxy.tcpConnect(options)

Open a TCP connection through a tunnel. Returns Web Streams.

const sock = await proxy.tcpConnect({
  host: "example.com",
  port: 443,
  tunnel: "us-east",  // Optional if defaultTunnel is set
})

// Write
const writer = sock.writable.getWriter()
await writer.write(new TextEncoder().encode("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"))
writer.releaseLock()

// Read
const reader = sock.readable.getReader()
const { value } = await reader.read()
console.log(new TextDecoder().decode(value))
reader.releaseLock()

await sock.close()

proxy.createUdpSocket(options?)

Create a UDP socket bound to a tunnel.

const udp = await proxy.createUdpSocket({ tunnel: "us-east" })

await udp.send(dnsQuery, { host: "1.1.1.1", port: 53 })
const { data, remote } = await udp.receive()
console.log(`Response from ${remote.host}:${remote.port}`)

await udp.close()

proxy.fetch(input, init?)

HTTP fetch routed through a tunnel. Same interface as the standard fetch().

const res = await proxy.fetch("https://example.com", {
  tunnel: "us-east",
  method: "POST",
  body: JSON.stringify({ key: "value" }),
  headers: { "Content-Type": "application/json" },
})

proxy.getUndiciDispatcher(tunnel?)

Get a custom Undici dispatcher for use with Node.js HTTP clients.

proxy.tunnelStats(idOrName)

Get tunnel statistics.

const stats = await proxy.tunnelStats("us-east")
// { connected: true, lastHandshakeAt: 1710..., txBytes: 1024, rxBytes: 2048, tcpOpen: 1, udpOpen: 0 }

proxy.close()

Shut down all tunnels and clean up resources.

Config Format

wireguard-proxy accepts standard WireGuard INI configs:

[Interface]
PrivateKey = <base64>
Address = 10.0.0.2/32
DNS = 1.1.1.1
MTU = 1420

[Peer]
PublicKey = <base64>
PresharedKey = <base64>           # Optional
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = 203.0.113.10:51820
PersistentKeepalive = 25

Or structured objects:

await proxy.addTunnel({
  name: "main",
  config: {
    privateKey: "yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=",
    address: ["10.0.0.2/32"],
    dns: ["1.1.1.1"],
    mtu: 1420,
    peers: [{
      publicKey: "xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=",
      endpoint: "203.0.113.10:51820",
      allowedIPs: ["0.0.0.0/0", "::/0"],
      persistentKeepalive: 25,
    }],
  },
})

You can also parse configs programmatically:

import { parseWireGuardConfig } from "wireguard-proxy"

const config = parseWireGuardConfig(iniText)
console.log(config.privateKey, config.peers[0].endpoint)

Multi-Tunnel Routing

const proxy = await createWireGuardProxy()

await proxy.addTunnel({ name: "ams", config: amsConfig })
await proxy.addTunnel({ name: "tyo", config: tyoConfig })

// Route specific requests through specific tunnels
const [amsRes, tyoRes] = await Promise.all([
  proxy.fetch("https://api.ipify.org", { tunnel: "ams" }),
  proxy.fetch("https://api.ipify.org", { tunnel: "tyo" }),
])

console.log("Amsterdam:", await amsRes.text())
console.log("Tokyo:", await tyoRes.text())

Error Handling

All errors extend WireGuardError with structured context:

import { WireGuardConfigError, WireGuardTcpError } from "wireguard-proxy"

try {
  await proxy.tcpConnect({ host: "example.com", port: 80 })
} catch (err) {
  if (err instanceof WireGuardTcpError) {
    console.log(err.tunnelId, err.operation, err.remoteAddress, err.retryable)
  }
}

Error types: WireGuardConfigError, WireGuardHandshakeError, WireGuardTimeoutError, WireGuardDnsError, WireGuardTcpError, WireGuardUdpError, WireGuardHttpError.

Project Structure

src/                    TypeScript public API
├── index.ts            Exports
├── types.ts            Type definitions
├── errors.ts           Error classes
├── proxy.ts            WireGuardProxy implementation
├── config/             Config parsing, validation, normalization
├── http/               Fetch wrapper and Undici dispatcher
└── native/             Native addon bindings

native/src/             Rust native engine
├── lib.rs              Node-API exports
├── engine.rs           Tunnel registry
├── tunnel.rs           WireGuard session + packet loop
├── virtual_net.rs      smoltcp ↔ boringtun bridge
├── tcp.rs              TCP flow state machine
├── udp.rs              UDP flow state machine
├── config.rs           Rust-side config deserialization
├── dns.rs              DNS policy
└── runtime.rs          Tokio runtime

examples/               Usage examples
test/                   Tests

Building from Source

# Prerequisites: Node.js/Bun, Rust toolchain

# Install dependencies
bun install

# Build native addon
cd native && cargo build --release
cp target/release/libwireguard_proxy_native.so wireguard-proxy-native.node

# Build TypeScript
bun run build:ts

# Run tests
bun test

Limitations (v1)

  • One peer per tunnel (no multi-peer routing)
  • IPv4 only for tunnel traffic
  • HTTP/1.1 only
  • No inbound connections or server mode
  • HTTPS over tunnel in Bun requires Node.js runtime
  • DNS resolution defaults to system resolver; tunnel DNS is opt-in

License

MIT — see LICENSE for details.