Skip to main content

readme.md for @push.rocks/smartvpn

A high-performance VPN solution with a TypeScript control plane and a Rust data plane daemon. Enterprise-ready client authentication, triple transport support (WebSocket + QUIC + WireGuard), and a typed hub API for managing clients from code.

πŸ” Noise IK mutual authentication β€” per-client X25519 keypairs, server-side registry πŸš€ Triple transport: WebSocket (Cloudflare-friendly), raw QUIC (datagrams), and WireGuard (standard protocol) πŸ›‘οΈ ACL engine β€” deny-overrides-allow IP filtering, aligned with SmartProxy conventions πŸ”€ PROXY protocol v2Β β€” real client IPs behind reverse proxies (HAProxy, SmartProxy, Cloudflare Spectrum) πŸ“Š Adaptive QoS: per-client rate limiting, priority queues, connection quality tracking πŸ”„ Hub API: one createClient() call generates keys, assigns IP, returns both SmartVPN + WireGuard configs πŸ“‘ Real-time telemetry: RTT, jitter, loss ratio, link health β€” all via typed APIs

Issue Reporting and Security

For reporting bugs, issues, or security vulnerabilities, please visit community.foss.global/. This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a code.foss.global/ account to submit Pull Requests directly.

Install πŸ“¦

pnpm install @push.rocks/smartvpn
# or
npm install @push.rocks/smartvpn

The package ships with pre-compiled Rust binaries for linux/amd64 and linux/arm64. No Rust toolchain required at runtime.

Architecture πŸ—οΈ

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     JSON-lines IPC     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚    TypeScript Control Plane  β”‚ ◄─────────────────────► β”‚     Rust Data Plane Daemon    β”‚
β”‚                              β”‚   stdio  or  Unix sock  β”‚                               β”‚
β”‚  VpnServer / VpnClient      β”‚                          β”‚  Noise IK handshake           β”‚
β”‚  Typed IPC commands          β”‚                          β”‚  XChaCha20-Poly1305           β”‚
β”‚  Config validation           β”‚                          β”‚  WS + QUIC + WireGuard        β”‚
β”‚  Hub: client management      β”‚                          β”‚  TUN device, IP pool, NAT     β”‚
β”‚  WireGuard .conf generation  β”‚                          β”‚  Rate limiting, ACLs, QoS     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Split-plane design β€” TypeScript handles orchestration, config, and DX; Rust handles every hot-path byte with zero-copy async I/O (tokio, mimalloc).

Quick Start πŸš€

1. Start a VPN Server (Hub)

import { VpnServer } from '@push.rocks/smartvpn';

const server = new VpnServer({ transport: { transport: 'stdio' } });
await server.start({
  listenAddr: '0.0.0.0:443',
  privateKey: '<server-noise-private-key-base64>',
  publicKey: '<server-noise-public-key-base64>',
  subnet: '10.8.0.0/24',
  transportMode: 'both', // WebSocket + QUIC simultaneously
  enableNat: true,
  dns: ['1.1.1.1', '8.8.8.8'],
});

2. Create a Client (One Call = Everything)

const bundle = await server.createClient({
  clientId: 'alice-laptop',
  tags: ['engineering'],
  security: {
    destinationAllowList: ['10.0.0.0/8'],        // can only reach internal network
    destinationBlockList: ['10.0.0.99'],           // except this host
    rateLimit: { bytesPerSec: 10_000_000, burstBytes: 20_000_000 },
  },
});

// bundle.smartvpnConfig  β†’ typed IVpnClientConfig, ready to use
// bundle.wireguardConfig β†’ standard WireGuard .conf string
// bundle.secrets         β†’ { noisePrivateKey, wgPrivateKey } β€” shown ONCE

3. Connect a Client

import { VpnClient } from '@push.rocks/smartvpn';

const client = new VpnClient({ transport: { transport: 'stdio' } });
await client.start();

const { assignedIp } = await client.connect(bundle.smartvpnConfig);
console.log(`Connected! VPN IP: ${assignedIp}`);

Features ✨

πŸ” Enterprise Authentication (Noise IK)

Every client authenticates with a Noise IK handshake (Noise_IK_25519_ChaChaPoly_BLAKE2s). The server verifies the client's static public key against its registry β€” unauthorized clients are rejected before any data flows.

  • Per-client X25519 keypair generated server-side
  • Client registry with enable/disable, expiry, tags
  • Key rotation with rotateClientKey() β€” generates new keys, returns fresh config bundle, disconnects old session

🌐 Triple Transport

Transport Protocol Best For
WebSocket TLS over TCP Firewall-friendly, Cloudflare compatible
QUIC UDP (via quinn) Low latency, datagram support for IP packets
WireGuard UDP (via boringtun) Standard WG clients (iOS, Android, wg-quick)

The server can run all three simultaneously with transportMode: 'both' (WS + QUIC) or 'wireguard'. Clients auto-negotiate with transport: 'auto' (tries QUIC first, falls back to WS).

πŸ›‘οΈ ACL Engine (SmartProxy-Aligned)

Security policies per client, using the same ipAllowList / ipBlockList naming convention as @push.rocks/smartproxy:

security: {
  ipAllowList: ['192.168.1.0/24'],          // source IPs allowed to connect
  ipBlockList: ['192.168.1.100'],            // deny overrides allow
  destinationAllowList: ['10.0.0.0/8'],      // VPN destinations permitted
  destinationBlockList: ['10.0.0.99'],        // deny overrides allow
  maxConnections: 5,
  rateLimit: { bytesPerSec: 1_000_000, burstBytes: 2_000_000 },
}

Supports exact IPs, CIDR, wildcards (192.168.1.*), and ranges (1.1.1.1-1.1.1.100).

πŸ”€ PROXY Protocol v2

When the VPN server sits behind a reverse proxy, enable PROXY protocol v2 to receive the real client IPΒ instead of the proxy's address. This makes ipAllowListΒ / ipBlockListΒ ACLs work correctly through load balancers.

await server.start({
  // ... other config ...
  proxyProtocol: true,                            // parse PP v2 headers on WS connections
  connectionIpBlockList: ['198.51.100.0/24'],     // server-wide block list (pre-handshake)
});

Two-phase ACL with real IPs:

Phase When What Happens Pre-handshake After TCP accept Server-level connectionIpBlockListΒ rejects known-bad IPs β€” zero crypto cost Post-handshake After Noise IK identifies client Per-client ipAllowListΒ / ipBlockListΒ checked against real source IP
    Parses the PP v2 binary header from raw TCP before WebSocket upgrade 5-second timeout protects against stalling attacks LOCAL command (proxy health checks) handled gracefully IPv4 and IPv6 addresses supported remoteAddrΒ field on IVpnClientInfoΒ exposes the real client IP for monitoring Security: must be falseΒ (default) when accepting direct connections β€” only enable behind a trusted proxy

    πŸ“Š Telemetry & QoS

    • Connection quality: Smoothed RTT, jitter, min/max RTT, loss ratio, link health (healthy / degraded / critical)
    • Adaptive keepalives: Interval adjusts based on link health (60s β†’ 30s β†’ 10s)
    • Per-client rate limiting: Token bucket with configurable bytes/sec and burst
    • Dead-peer detection: 180s inactivity timeout
    • MTU management: Automatic overhead calculation (IP+TCP+WS+Noise = 79 bytes)

    πŸ”„ Hub Client Management

    The server acts as a hub β€” one API to manage all clients:

    // Create (generates keys, assigns IP, returns config bundle)
    const bundle = await server.createClient({ clientId: 'bob-phone' });
    
    // Read
    const entry = await server.getClient('bob-phone');
    const all   = await server.listRegisteredClients();
    
    // Update (ACLs, tags, description, rate limits...)
    await server.updateClient('bob-phone', {
      security: { destinationAllowList: ['0.0.0.0/0'] },
      tags: ['mobile', 'field-ops'],
    });
    
    // Enable / Disable
    await server.disableClient('bob-phone'); // disconnects + blocks reconnection
    await server.enableClient('bob-phone');
    
    // Key rotation
    const newBundle = await server.rotateClientKey('bob-phone');
    
    // Export config (without secrets)
    const wgConf = await server.exportClientConfig('bob-phone', 'wireguard');
    
    // Remove
    await server.removeClient('bob-phone');
    

    πŸ“ WireGuard Config Generation

    Generate standard .conf files for any WireGuard client:

    import { WgConfigGenerator } from '@push.rocks/smartvpn';
    
    const conf = WgConfigGenerator.generateClientConfig({
      privateKey: '<client-wg-private-key>',
      address: '10.8.0.2/24',
      dns: ['1.1.1.1'],
      peer: {
        publicKey: '<server-wg-public-key>',
        endpoint: 'vpn.example.com:51820',
        allowedIps: ['0.0.0.0/0'],
        persistentKeepalive: 25,
      },
    });
    // β†’ standard WireGuard .conf compatible with wg-quick, iOS, Android
    

    πŸ–₯️ System Service Installation

    import { VpnInstaller } from '@push.rocks/smartvpn';
    
    const unit = VpnInstaller.generateServiceUnit({
      mode: 'server',
      configPath: '/etc/smartvpn/server.json',
    });
    // unit.platform  β†’ 'linux' | 'macos'
    // unit.content   β†’ systemd unit file or launchd plist
    // unit.installPath β†’ /etc/systemd/system/smartvpn-server.service
    

    API Reference πŸ“–

    Classes

    Class Description
    VpnServer Manages the Rust daemon in server mode. Hub methods for client CRUD.
    VpnClient Manages the Rust daemon in client mode. Connect, disconnect, telemetry.
    VpnBridge<T> Low-level typed IPC bridge (stdio or Unix socket).
    VpnConfig Static config validation and file I/O.
    VpnInstaller Generates systemd/launchd service files.
    WgConfigGenerator Generates standard WireGuard .conf files.

    Key Interfaces

    Interface Purpose
    IVpnServerConfig Server configuration (listen addr, keys, subnet, transport mode, clients)clients, proxy protocol)
    IVpnClientConfig Client configuration (server URL, keys, transport, WG options)
    IClientEntry Server-side client definition (ID, keys, security, priority, tags, expiry)
    IClientSecurity Per-client ACLs and rate limits (SmartProxy-aligned naming)
    IClientRateLimit Rate limiting config (bytesPerSec, burstBytes)
    IClientConfigBundle Full config bundle returned by createClient()
    IVpnClientInfo Connected client info (IP, stats, authenticated key)key, remote addr)
    IVpnConnectionQuality RTT, jitter, loss ratio, link health
    IVpnKeypair Base64-encoded public/private key pair

    Server IPC Commands

    Command Description
    start / stop Start/stop the VPN listener
    createClient Generate keys, assign IP, return config bundle
    removeClient / getClient / listRegisteredClients Client registry CRUD
    updateClient / enableClient / disableClient Modify client state
    rotateClientKey Fresh keypairs + new config bundle
    exportClientConfig Re-export as SmartVPN config or WireGuard .conf
    listClients / disconnectClient Manage live connections
    setClientRateLimit / removeClientRateLimit Runtime rate limit adjustments
    getStatus / getStatistics / getClientTelemetry Monitoring
    generateKeypair / generateWgKeypair / generateClientKeypair Key generation
    addWgPeer / removeWgPeer / listWgPeers WireGuard peer management

    Client IPC Commands

    Command Description
    connect / disconnect Manage the tunnel
    getStatus / getStatistics Connection state and traffic stats
    getConnectionQuality RTT, jitter, loss, link health
    getMtuInfo MTU and overhead details

    Transport Modes πŸ”€

    Server Configuration

    // WebSocket only
    { transportMode: 'websocket', listenAddr: '0.0.0.0:443' }
    
    // QUIC only
    { transportMode: 'quic', listenAddr: '0.0.0.0:443' }
    
    // Both (WS + QUIC on same or different ports)
    { transportMode: 'both', listenAddr: '0.0.0.0:443', quicListenAddr: '0.0.0.0:4433' }
    
    // WireGuard
    { transportMode: 'wireguard', wgListenPort: 51820, wgPeers: [...] }
    

    Client Configuration

    // Auto (tries QUIC first, falls back to WS)
    { transport: 'auto', serverUrl: 'wss://vpn.example.com' }
    
    // Explicit QUIC with certificate pinning
    { transport: 'quic', serverUrl: '1.2.3.4:4433', serverCertHash: '<sha256-base64>' }
    
    // WireGuard
    { transport: 'wireguard', wgPrivateKey: '...', wgEndpoint: 'vpn.example.com:51820', ... }
    

    Cryptography πŸ”‘

    Layer Algorithm Purpose
    Handshake Noise IK (X25519 + ChaChaPoly + BLAKE2s) Mutual authentication + key exchange
    Transport Noise transport state (ChaChaPoly) All post-handshake data encryption
    Additional XChaCha20-Poly1305 Extended nonce space for data-at-rest
    WireGuard X25519 + ChaCha20-Poly1305 (via boringtun) Standard WireGuard crypto

    Binary Protocol πŸ“‘

    All frames use [type:1B][length:4B][payload:NB] with a 64KB max payload:

    Type Hex Direction Description
    HandshakeInit 0x01 Client β†’ Server Noise IK first message
    HandshakeResp 0x02 Server β†’ Client Noise IK response
    IpPacket 0x10 Bidirectional Encrypted tunnel data
    Keepalive 0x20 Client β†’ Server App-level keepalive (not WS ping)
    KeepaliveAck 0x21 Server β†’ Client Keepalive response with RTT payload
    Disconnect 0x3F Bidirectional Graceful disconnect

    Development πŸ› οΈ

    # Install dependencies
    pnpm install
    
    # Build (TypeScript + Rust cross-compile)
    pnpm build
    
    # Run all tests (79 TS + 121129 Rust = 200208 tests)
    pnpm test
    
    # Run Rust tests directly
    cd rust && cargo test
    
    # Run a specific TS test
    tstest test/test.flowcontrol.node.ts --verbose
    

    Project Structure

    smartvpn/
    β”œβ”€β”€ ts/                         # TypeScript control plane
    β”‚   β”œβ”€β”€ index.ts                # All exports
    β”‚   β”œβ”€β”€ smartvpn.interfaces.ts  # Interfaces, types, IPC command maps
    β”‚   β”œβ”€β”€ smartvpn.classes.vpnserver.ts
    β”‚   β”œβ”€β”€ smartvpn.classes.vpnclient.ts
    β”‚   β”œβ”€β”€ smartvpn.classes.vpnbridge.ts
    β”‚   β”œβ”€β”€ smartvpn.classes.vpnconfig.ts
    β”‚   β”œβ”€β”€ smartvpn.classes.vpninstaller.ts
    β”‚   └── smartvpn.classes.wgconfig.ts
    β”œβ”€β”€ rust/                       # Rust data plane daemon
    β”‚   └── src/
    β”‚       β”œβ”€β”€ main.rs             # CLI entry point
    β”‚       β”œβ”€β”€ server.rs           # VPN server + hub methods
    β”‚       β”œβ”€β”€ client.rs           # VPN client
    β”‚       β”œβ”€β”€ crypto.rs           # Noise IK + XChaCha20
    β”‚       β”œβ”€β”€ client_registry.rs  # Client database
    β”‚       β”œβ”€β”€ acl.rs              # ACL engine
    β”‚       β”œβ”€β”€ proxy_protocol.rs   # PROXY protocol v2 parser
    β”‚       β”œβ”€β”€ management.rs       # JSON-lines IPC
    β”‚       β”œβ”€β”€ transport.rs        # WebSocket transport
    β”‚       β”œβ”€β”€ quic_transport.rs   # QUIC transport
    β”‚       β”œβ”€β”€ wireguard.rs        # WireGuard (boringtun)
    β”‚       β”œβ”€β”€ codec.rs            # Binary frame protocol
    β”‚       β”œβ”€β”€ keepalive.rs        # Adaptive keepalives
    β”‚       β”œβ”€β”€ ratelimit.rs        # Token bucket
    β”‚       └── ...                 # tunnel, network, telemetry, qos, mtu, reconnect
    β”œβ”€β”€ test/                       # 9 test files (79 tests)
    β”œβ”€β”€ dist_ts/                    # Compiled TypeScript
    └── dist_rust/                  # Cross-compiled binaries (linux amd64 + arm64)
    

    This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the license file.

    Please note: The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.

    Trademarks

    This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.

    Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.

    Company Information

    Task Venture Capital GmbH Registered at District Court Bremen HRB 35230 HB, Germany

    By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.