@push.rocks/smartvpn 1.18.0 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartvpn',
6
- version: '1.18.0',
6
+ version: '1.19.0',
7
7
  description: 'A VPN solution with TypeScript control plane and Rust data plane daemon'
8
8
  };
9
9
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSxzQkFBc0I7SUFDNUIsT0FBTyxFQUFFLFFBQVE7SUFDakIsV0FBVyxFQUFFLHlFQUF5RTtDQUN2RixDQUFBIn0=
@@ -75,8 +75,9 @@ export interface IVpnServerConfig {
75
75
  /** Enable NAT/masquerade for client traffic */
76
76
  enableNat?: boolean;
77
77
  /** Forwarding mode: 'tun' (kernel TUN, requires root), 'socket' (userspace NAT),
78
+ * 'bridge' (L2 bridge to host LAN), 'hybrid' (per-client socket+bridge),
78
79
  * or 'testing' (monitoring only). Default: 'testing'. */
79
- forwardingMode?: 'tun' | 'socket' | 'bridge' | 'testing';
80
+ forwardingMode?: 'tun' | 'socket' | 'bridge' | 'hybrid' | 'testing';
80
81
  /** Default rate limit for new clients (bytes/sec). Omit for unlimited. */
81
82
  defaultRateLimitBytesPerSec?: number;
82
83
  /** Default burst size for new clients (bytes). Omit for unlimited. */
@@ -300,6 +301,18 @@ export interface IClientEntry {
300
301
  expiresAt?: string;
301
302
  /** Assigned VPN IP address (set by server) */
302
303
  assignedIp?: string;
304
+ /** If true, client gets a host network IP via bridge mode (L2 to LAN).
305
+ * If false (default), client gets a VPN subnet IP via socket/NAT mode. */
306
+ useHostIp?: boolean;
307
+ /** If true and useHostIp is true, obtain IP via DHCP relay.
308
+ * If false or omitted, use staticIp or auto-assign from bridge IP range. */
309
+ useDhcp?: boolean;
310
+ /** Static LAN IP when useHostIp is true and useDhcp is false. */
311
+ staticIp?: string;
312
+ /** If true, assign this client to a specific 802.1Q VLAN on the bridge. */
313
+ forceVlan?: boolean;
314
+ /** 802.1Q VLAN ID (1-4094). Required when forceVlan is true. */
315
+ vlanId?: number;
303
316
  }
304
317
  /**
305
318
  * Complete client config bundle — returned by createClient() and rotateClientKey().
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@push.rocks/smartvpn",
3
- "version": "1.18.0",
3
+ "version": "1.19.0",
4
4
  "private": false,
5
5
  "description": "A VPN solution with TypeScript control plane and Rust data plane daemon",
6
6
  "type": "module",
package/readme.md CHANGED
@@ -2,17 +2,19 @@
2
2
 
3
3
  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.
4
4
 
5
- 🔐 **Noise IK** mutual authentication — per-client X25519 keypairs, server-side registry
6
- 🚀 **Triple transport**: WebSocket (Cloudflare-friendly), raw **QUIC** (datagrams), and **WireGuard** (standard protocol)
7
- 🛡️ **ACL engine** — deny-overrides-allow IP filtering, aligned with SmartProxy conventions
8
- 🔀 **PROXY protocol v2** — real client IPs behind reverse proxies (HAProxy, SmartProxy, Cloudflare Spectrum)
9
- 📊 **Per-transport metrics**: active clients and total connections broken down by websocket, QUIC, and WireGuard
10
- 🔄 **Hub API**: one `createClient()` call generates keys, assigns IP, returns both SmartVPN + WireGuard configs
11
- 📡 **Real-time telemetry**: RTT, jitter, loss ratio, link health — all via typed APIs
12
- 🌐 **Unified forwarding pipeline**: all transports share the same engine — TUN (kernel), userspace NAT (no root), L2 bridge, or testing mode
13
- 🏠 **Bridge mode**: VPN clients get IPs from your LAN subnet — seamlessly bridge remote clients onto a physical network
14
- 🎯 **Destination routing policy**: force-target, block, or allow traffic per destination with nftables integration
15
- **Handshake-driven WireGuard state**: peers appear as "connected" only after a successful WireGuard handshake, and auto-disconnect on idle timeout
5
+ - 🔐 **Noise IK** mutual authentication — per-client X25519 keypairs, server-side registry
6
+ - 🚀 **Triple transport**: WebSocket (Cloudflare-friendly), raw **QUIC** (datagrams), and **WireGuard** (standard protocol)
7
+ - 🛡️ **ACL engine** — deny-overrides-allow IP filtering, aligned with SmartProxy conventions
8
+ - 🔀 **PROXY protocol v2** — real client IPs behind reverse proxies (HAProxy, SmartProxy, Cloudflare Spectrum)
9
+ - 📊 **Per-transport metrics**: active clients and total connections broken down by websocket, QUIC, and WireGuard
10
+ - 🔄 **Hub API**: one `createClient()` call generates keys, assigns IP, returns both SmartVPN + WireGuard configs
11
+ - 📡 **Real-time telemetry**: RTT, jitter, loss ratio, link health — all via typed APIs
12
+ - 🌐 **Unified forwarding pipeline**: all transports share the same engine — TUN (kernel), userspace NAT (no root), L2 bridge, hybrid, or testing mode
13
+ - 🏠 **Bridge mode**: VPN clients get IPs from your LAN subnet — seamlessly bridge remote clients onto a physical network
14
+ - 🔀 **Hybrid mode**: per-client routing — some clients bridge to the LAN, others use userspace NAT, all on the same server
15
+ - 🏷️ **VLAN support**: assign individual clients to 802.1Q VLANs on the bridge
16
+ - 🎯 **Destination routing policy**: force-target, block, or allow traffic per destination with nftables integration
17
+ - ⚡ **Handshake-driven WireGuard state**: peers appear as "connected" only after a successful WireGuard handshake, and auto-disconnect on idle timeout
16
18
 
17
19
  ## Issue Reporting and Security
18
20
 
@@ -85,7 +87,7 @@ await server.start({
85
87
  publicKey: '<server-noise-public-key-base64>',
86
88
  subnet: '10.8.0.0/24',
87
89
  transportMode: 'all', // WebSocket + QUIC + WireGuard simultaneously (default)
88
- forwardingMode: 'tun', // 'tun' | 'socket' | 'bridge' | 'testing'
90
+ forwardingMode: 'tun', // 'tun' | 'socket' | 'bridge' | 'hybrid' | 'testing'
89
91
  wgPrivateKey: '<server-wg-private-key-base64>', // required for WireGuard transport
90
92
  enableNat: true,
91
93
  dns: ['1.1.1.1', '8.8.8.8'],
@@ -267,13 +269,14 @@ await server.start({
267
269
 
268
270
  ### 📦 Packet Forwarding Modes
269
271
 
270
- SmartVPN supports four forwarding modes, configurable per-server and per-client:
272
+ SmartVPN supports five forwarding modes, configurable per-server:
271
273
 
272
274
  | Mode | Flag | Description | Root Required |
273
275
  |------|------|-------------|---------------|
274
276
  | **TUN** | `'tun'` | Kernel TUN device — real packet forwarding with system routing | ✅ Yes |
275
277
  | **Userspace NAT** | `'socket'` | Userspace TCP/UDP proxy via `connect(2)` — no TUN, no root needed | ❌ No |
276
278
  | **Bridge** | `'bridge'` | L2 bridge — VPN clients get IPs from a physical LAN subnet | ✅ Yes |
279
+ | **Hybrid** | `'hybrid'` | Per-client routing: some clients use socket NAT, others use bridge — both engines run simultaneously | ✅ Yes |
277
280
  | **Testing** | `'testing'` | Monitoring only — packets are counted but not forwarded | ❌ No |
278
281
 
279
282
  ```typescript
@@ -294,6 +297,13 @@ await server.start({
294
297
  bridgeIpRangeEnd: 250,
295
298
  });
296
299
 
300
+ // Server with hybrid mode — per-client routing
301
+ await server.start({
302
+ // ...
303
+ forwardingMode: 'hybrid',
304
+ bridgePhysicalInterface: 'eth0', // for bridge clients
305
+ });
306
+
297
307
  // Client with TUN device
298
308
  const { assignedIp } = await client.connect({
299
309
  // ...
@@ -305,6 +315,52 @@ The **userspace NAT** mode extracts destination IP/port from IP packets, opens a
305
315
 
306
316
  The **bridge** mode assigns VPN clients IPs from a real LAN subnet instead of a virtual VPN subnet. Clients appear as if they're directly on the physical network — perfect for remote access to home labs, office networks, or IoT devices.
307
317
 
318
+ The **hybrid** mode runs both engines simultaneously with a **per-client routing table**. Each client's `useHostIp` flag determines whether its packets go through the bridge (L2, LAN IP) or socket NAT (userspace, VPN IP). This is ideal when most clients need internet NAT but some need direct LAN access.
319
+
320
+ ### 🏠 Per-Client Bridge & VLAN Settings
321
+
322
+ When using `bridge` or `hybrid` mode, each client can be individually configured for LAN bridging, static IPs, DHCP, and 802.1Q VLAN assignment:
323
+
324
+ ```typescript
325
+ // Client that bridges to the LAN with a static IP
326
+ await server.createClient({
327
+ clientId: 'office-printer',
328
+ useHostIp: true, // bridge to LAN instead of VPN subnet
329
+ staticIp: '192.168.1.210', // fixed LAN IP
330
+ });
331
+
332
+ // Client that gets a LAN IP via DHCP
333
+ await server.createClient({
334
+ clientId: 'roaming-laptop',
335
+ useHostIp: true,
336
+ useDhcp: true, // obtain IP from LAN DHCP server
337
+ });
338
+
339
+ // Client on a specific VLAN
340
+ await server.createClient({
341
+ clientId: 'iot-sensor',
342
+ useHostIp: true,
343
+ forceVlan: true,
344
+ vlanId: 100, // 802.1Q VLAN ID (1-4094)
345
+ });
346
+
347
+ // Regular NAT client (default, no bridge)
348
+ await server.createClient({
349
+ clientId: 'remote-worker',
350
+ // useHostIp defaults to false → uses socket NAT
351
+ });
352
+ ```
353
+
354
+ | Field | Type | Description |
355
+ |-------|------|-------------|
356
+ | `useHostIp` | `boolean` | `true` = bridge to LAN (host IP), `false` = VPN subnet via NAT (default) |
357
+ | `useDhcp` | `boolean` | When `useHostIp` is true, obtain IP via DHCP relay instead of static/auto-assign |
358
+ | `staticIp` | `string` | Fixed LAN IP when `useHostIp` is true and `useDhcp` is false |
359
+ | `forceVlan` | `boolean` | Assign this client to a specific 802.1Q VLAN on the bridge |
360
+ | `vlanId` | `number` | VLAN ID (1-4094), required when `forceVlan` is true |
361
+
362
+ VLAN support uses Linux bridge VLAN filtering — each client's TAP port gets tagged with the specified VLAN ID, isolating traffic at Layer 2.
363
+
308
364
  ### 📊 Telemetry & QoS
309
365
 
310
366
  - **Connection quality**: Smoothed RTT, jitter, min/max RTT, loss ratio, link health (`healthy` / `degraded` / `critical`)
@@ -473,9 +529,9 @@ server.on('reconnected', () => { /* socket transport reconnected */ });
473
529
 
474
530
  | Interface | Purpose |
475
531
  |-----------|---------|
476
- | `IVpnServerConfig` | Server configuration (listen addr, keys, subnet, transport mode, forwarding mode incl. bridge, clients, proxy protocol, destination policy) |
532
+ | `IVpnServerConfig` | Server configuration (listen addr, keys, subnet, transport mode, forwarding mode incl. bridge/hybrid, clients, proxy protocol, destination policy) |
477
533
  | `IVpnClientConfig` | Client configuration (server URL, keys, transport, forwarding mode, WG options, client-defined tags) |
478
- | `IClientEntry` | Server-side client definition (ID, keys, security, priority, server/client tags, expiry) |
534
+ | `IClientEntry` | Server-side client definition (ID, keys, security, priority, server/client tags, expiry, bridge/VLAN settings) |
479
535
  | `IClientSecurity` | Per-client ACLs, rate limits, and destination policy override (SmartProxy-aligned naming) |
480
536
  | `IClientRateLimit` | Rate limiting config (bytesPerSec, burstBytes) |
481
537
  | `IClientConfigBundle` | Full config bundle returned by `createClient()` — includes SmartVPN config, WireGuard .conf, and secrets |
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartvpn',
6
- version: '1.18.0',
6
+ version: '1.19.0',
7
7
  description: 'A VPN solution with TypeScript control plane and Rust data plane daemon'
8
8
  }
@@ -92,8 +92,9 @@ export interface IVpnServerConfig {
92
92
  /** Enable NAT/masquerade for client traffic */
93
93
  enableNat?: boolean;
94
94
  /** Forwarding mode: 'tun' (kernel TUN, requires root), 'socket' (userspace NAT),
95
+ * 'bridge' (L2 bridge to host LAN), 'hybrid' (per-client socket+bridge),
95
96
  * or 'testing' (monitoring only). Default: 'testing'. */
96
- forwardingMode?: 'tun' | 'socket' | 'bridge' | 'testing';
97
+ forwardingMode?: 'tun' | 'socket' | 'bridge' | 'hybrid' | 'testing';
97
98
  /** Default rate limit for new clients (bytes/sec). Omit for unlimited. */
98
99
  defaultRateLimitBytesPerSec?: number;
99
100
  /** Default burst size for new clients (bytes). Omit for unlimited. */
@@ -361,6 +362,21 @@ export interface IClientEntry {
361
362
  expiresAt?: string;
362
363
  /** Assigned VPN IP address (set by server) */
363
364
  assignedIp?: string;
365
+
366
+ // Per-client bridge/host-IP settings
367
+
368
+ /** If true, client gets a host network IP via bridge mode (L2 to LAN).
369
+ * If false (default), client gets a VPN subnet IP via socket/NAT mode. */
370
+ useHostIp?: boolean;
371
+ /** If true and useHostIp is true, obtain IP via DHCP relay.
372
+ * If false or omitted, use staticIp or auto-assign from bridge IP range. */
373
+ useDhcp?: boolean;
374
+ /** Static LAN IP when useHostIp is true and useDhcp is false. */
375
+ staticIp?: string;
376
+ /** If true, assign this client to a specific 802.1Q VLAN on the bridge. */
377
+ forceVlan?: boolean;
378
+ /** 802.1Q VLAN ID (1-4094). Required when forceVlan is true. */
379
+ vlanId?: number;
364
380
  }
365
381
 
366
382
  /**