@push.rocks/smartvpn 1.3.0 → 1.4.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.3.0',
6
+ version: '1.4.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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSxzQkFBc0I7SUFDNUIsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLHlFQUF5RTtDQUN2RixDQUFBIn0=
@@ -10,8 +10,11 @@ export class VpnConfig {
10
10
  if (!config.serverUrl) {
11
11
  throw new Error('VpnConfig: serverUrl is required');
12
12
  }
13
- if (!config.serverUrl.startsWith('wss://') && !config.serverUrl.startsWith('ws://')) {
14
- throw new Error('VpnConfig: serverUrl must start with wss:// or ws://');
13
+ // For QUIC-only transport, serverUrl is a host:port address; for WebSocket/auto it must be ws:// or wss://
14
+ if (config.transport !== 'quic') {
15
+ if (!config.serverUrl.startsWith('wss://') && !config.serverUrl.startsWith('ws://')) {
16
+ throw new Error('VpnConfig: serverUrl must start with wss:// or ws:// (for WebSocket transport)');
17
+ }
15
18
  }
16
19
  if (!config.serverPublicKey) {
17
20
  throw new Error('VpnConfig: serverPublicKey is required');
@@ -95,4 +98,4 @@ export class VpnConfig {
95
98
  return !isNaN(prefixNum) && prefixNum >= 0 && prefixNum <= 32;
96
99
  }
97
100
  }
98
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnR2cG4uY2xhc3Nlcy52cG5jb25maWcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydHZwbi5jbGFzc2VzLnZwbmNvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHVCQUF1QixDQUFDO0FBTWpEOztHQUVHO0FBQ0gsTUFBTSxPQUFPLFNBQVM7SUFDcEI7O09BRUc7SUFDSSxNQUFNLENBQUMsb0JBQW9CLENBQUMsTUFBd0I7UUFDekQsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUN0QixNQUFNLElBQUksS0FBSyxDQUFDLGtDQUFrQyxDQUFDLENBQUM7UUFDdEQsQ0FBQztRQUNELElBQUksQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsVUFBVSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7WUFDcEYsTUFBTSxJQUFJLEtBQUssQ0FBQyxzREFBc0QsQ0FBQyxDQUFDO1FBQzFFLENBQUM7UUFDRCxJQUFJLENBQUMsTUFBTSxDQUFDLGVBQWUsRUFBRSxDQUFDO1lBQzVCLE1BQU0sSUFBSSxLQUFLLENBQUMsd0NBQXdDLENBQUMsQ0FBQztRQUM1RCxDQUFDO1FBQ0QsSUFBSSxNQUFNLENBQUMsR0FBRyxLQUFLLFNBQVMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxHQUFHLEdBQUcsR0FBRyxJQUFJLE1BQU0sQ0FBQyxHQUFHLEdBQUcsS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUN6RSxNQUFNLElBQUksS0FBSyxDQUFDLDhDQUE4QyxDQUFDLENBQUM7UUFDbEUsQ0FBQztRQUNELElBQUksTUFBTSxDQUFDLHFCQUFxQixLQUFLLFNBQVMsSUFBSSxNQUFNLENBQUMscUJBQXFCLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDbkYsTUFBTSxJQUFJLEtBQUssQ0FBQywrQ0FBK0MsQ0FBQyxD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
101
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnR2cG4uY2xhc3Nlcy52cG5jb25maWcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydHZwbi5jbGFzc2VzLnZwbmNvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHVCQUF1QixDQUFDO0FBTWpEOztHQUVHO0FBQ0gsTUFBTSxPQUFPLFNBQVM7SUFDcEI7O09BRUc7SUFDSSxNQUFNLENBQUMsb0JBQW9CLENBQUMsTUFBd0I7UUFDekQsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLEVBQUUsQ0FBQztZQUN0QixNQUFNLElBQUksS0FBSyxDQUFDLGtDQUFrQyxDQUFDLENBQUM7UUFDdEQsQ0FBQztRQUNELDJHQUEyRztRQUMzRyxJQUFJLE1BQU0sQ0FBQyxTQUFTLEtBQUssTUFBTSxFQUFFLENBQUM7WUFDaEMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsVUFBVSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQztnQkFDcEYsTUFBTSxJQUFJLEtBQUssQ0FBQyxnRkFBZ0YsQ0FBQyxDQUFDO1lBQ3BHLENBQUM7UUFDSCxDQUFDO1FBQ0QsSUFBSSxDQUFDLE1BQU0sQ0FBQyxlQUFlLEVBQUUsQ0FBQztZQUM1QixNQUFNLElBQUksS0FBSyxDQUFDLHdDQUF3QyxDQUFDLENBQUM7UUFDNUQsQ0FBQztRQUNELElBQUksTUFBTSxDQUFDLEdBQUcsS0FBSyxTQUFTLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxHQUFHLEdBQUcsSUFBSSxNQUFNLENBQUMsR0FBRyxHQUFHLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDekUsTUFBTSxJQUFJLEtBQUssQ0FBQyw4Q0FBOEMsQ0FBQyxDQUFDO1FBQ2xFLENBQUM7UUFDRCxJQUFJLE1BQU0sQ0FBQyxxQkFBcUIsS0FBSyxTQUFTLElBQUksTUFBTSxD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
@@ -21,6 +21,10 @@ export interface IVpnClientConfig {
21
21
  mtu?: number;
22
22
  /** Keepalive interval in seconds (default: 30) */
23
23
  keepaliveIntervalSecs?: number;
24
+ /** Transport protocol: 'auto' (default, tries QUIC then WS), 'websocket', or 'quic' */
25
+ transport?: 'auto' | 'websocket' | 'quic';
26
+ /** For QUIC: SHA-256 hash of server certificate (base64) for cert pinning */
27
+ serverCertHash?: string;
24
28
  }
25
29
  export interface IVpnClientOptions {
26
30
  transport: TVpnTransportOptions;
@@ -51,6 +55,12 @@ export interface IVpnServerConfig {
51
55
  defaultRateLimitBytesPerSec?: number;
52
56
  /** Default burst size for new clients (bytes). Omit for unlimited. */
53
57
  defaultBurstBytes?: number;
58
+ /** Transport mode: 'both' (default, WS+QUIC), 'websocket', or 'quic' */
59
+ transportMode?: 'websocket' | 'quic' | 'both';
60
+ /** QUIC listen address (host:port). Defaults to listenAddr. */
61
+ quicListenAddr?: string;
62
+ /** QUIC idle timeout in seconds (default: 30) */
63
+ quicIdleTimeoutSecs?: number;
54
64
  }
55
65
  export interface IVpnServerOptions {
56
66
  transport: TVpnTransportOptions;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@push.rocks/smartvpn",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "private": false,
5
5
  "description": "A VPN solution with TypeScript control plane and Rust data plane daemon",
6
6
  "type": "module",
@@ -11,6 +11,7 @@
11
11
  "typings": "dist_ts/index.d.ts",
12
12
  "scripts": {
13
13
  "build": "(tsbuild tsfolders --allowimplicitany) && (tsrust)",
14
+ "test:before": "(tsrust)",
14
15
  "test": "tstest test/ --verbose",
15
16
  "buildDocs": "tsdoc"
16
17
  },
@@ -28,15 +29,15 @@
28
29
  ],
29
30
  "license": "MIT",
30
31
  "dependencies": {
31
- "@push.rocks/smartrust": "^1.3.0",
32
- "@push.rocks/smartpath": "^5.0.18"
32
+ "@push.rocks/smartpath": "^6.0.0",
33
+ "@push.rocks/smartrust": "^1.3.2"
33
34
  },
34
35
  "devDependencies": {
35
- "@git.zone/tsbuild": "^2.2.12",
36
- "@git.zone/tsrun": "^1.3.3",
37
- "@git.zone/tstest": "^1.0.96",
38
- "@git.zone/tsrust": "^1.0.29",
39
- "@types/node": "^22.0.0"
36
+ "@git.zone/tsbuild": "^4.3.0",
37
+ "@git.zone/tsrun": "^2.0.1",
38
+ "@git.zone/tsrust": "^1.3.0",
39
+ "@git.zone/tstest": "^3.5.0",
40
+ "@types/node": "^25.5.0"
40
41
  },
41
42
  "files": [
42
43
  "ts/**/*",
package/readme.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  A high-performance VPN with a **TypeScript control plane** and a **Rust data plane daemon**. Manage VPN connections with clean, fully-typed APIs while all networking heavy lifting — encryption, tunneling, QoS, rate limiting — runs at native speed in Rust.
4
4
 
5
+ 🔒 **Noise NK** handshake + **XChaCha20-Poly1305** encryption
6
+ 🚀 **Dual transport**: WebSocket (Cloudflare-friendly) and raw **QUIC** (with datagram support)
7
+ 📊 **Adaptive QoS**: packet classification, priority queues, per-client rate limiting
8
+ 🔄 **Auto-transport**: tries QUIC first, falls back to WebSocket seamlessly
9
+ 📡 **Real-time telemetry**: RTT, jitter, loss, link health — all exposed via typed APIs
10
+
5
11
  ## Issue Reporting and Security
6
12
 
7
13
  For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://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/](https://code.foss.global/) account to submit Pull Requests directly.
@@ -17,11 +23,13 @@ pnpm install @push.rocks/smartvpn
17
23
  ```
18
24
  TypeScript (control plane) Rust (data plane)
19
25
  ┌──────────────────────────┐ ┌────────────────────────────────────┐
20
- │ VpnClient / VpnServer │ │ smartvpn_daemon
26
+ │ VpnClient / VpnServer │ │ smartvpn_daemon
21
27
  │ └─ VpnBridge │──stdio/──▶ │ ├─ management (JSON IPC) │
22
- │ └─ RustBridge │ socket │ ├─ transport (WebSocket/TLS)
23
- │ (smartrust) │ │ ├─ crypto (Noise NK + XCha20)
24
- └──────────────────────────┘ │ ├─ codec (binary framing)
28
+ │ └─ RustBridge │ socket │ ├─ transport_trait (abstraction)
29
+ │ (smartrust) │ │ ├─ transport (WebSocket/TLS)
30
+ └──────────────────────────┘ │ │ └─ quic_transport (QUIC/UDP)
31
+ │ ├─ crypto (Noise NK + XCha20) │
32
+ │ ├─ codec (binary framing) │
25
33
  │ ├─ keepalive (adaptive state FSM) │
26
34
  │ ├─ telemetry (RTT/jitter/loss) │
27
35
  │ ├─ qos (classify + priority Q) │
@@ -37,8 +45,10 @@ TypeScript (control plane) Rust (data plane)
37
45
 
38
46
  | Decision | Choice | Why |
39
47
  |----------|--------|-----|
40
- | Transport | WebSocket over HTTPS | Works through Cloudflare and other terminating proxies |
41
- | Encryption | Noise NK + XChaCha20-Poly1305 | Strong forward secrecy, large nonce space (no counter needed) |
48
+ | Transport | WebSocket + QUIC (dual) | WS works through Cloudflare; QUIC gives lower latency + unreliable datagrams |
49
+ | Auto-transport | QUIC first, WS fallback | Best performance when QUIC is available, graceful degradation when it's not |
50
+ | Encryption | Noise NK + XChaCha20-Poly1305 | Strong forward secrecy, large nonce space (no counter sync needed) |
51
+ | QUIC auth | Certificate hash pinning | WireGuard-style trust model — no CA needed, just pin the server cert hash |
42
52
  | Keepalive | Adaptive app-level pings | Cloudflare drops WS pings; interval adapts to link health (10–60s) |
43
53
  | QoS | Packet classification + priority queues | DNS/SSH/ICMP always drain first; bulk flows get deprioritized |
44
54
  | Rate limiting | Per-client token bucket | Byte-granular, dynamically reconfigurable via IPC |
@@ -89,6 +99,39 @@ await client.disconnect();
89
99
  client.stop();
90
100
  ```
91
101
 
102
+ ### VPN Client with QUIC
103
+
104
+ ```typescript
105
+ import { VpnClient } from '@push.rocks/smartvpn';
106
+
107
+ // Explicit QUIC — serverUrl is host:port, pinned by cert hash
108
+ const quicClient = new VpnClient({
109
+ transport: { transport: 'stdio' },
110
+ });
111
+
112
+ await quicClient.start();
113
+
114
+ const { assignedIp } = await quicClient.connect({
115
+ serverUrl: 'vpn.example.com:443',
116
+ serverPublicKey: 'BASE64_SERVER_PUBLIC_KEY',
117
+ transport: 'quic',
118
+ serverCertHash: 'BASE64_SHA256_CERT_HASH', // printed by server on startup
119
+ });
120
+
121
+ // Or use auto-transport: tries QUIC first (3s timeout), falls back to WS
122
+ const autoClient = new VpnClient({
123
+ transport: { transport: 'stdio' },
124
+ });
125
+
126
+ await autoClient.start();
127
+
128
+ await autoClient.connect({
129
+ serverUrl: 'wss://vpn.example.com/tunnel', // WS URL — host:port extracted for QUIC attempt
130
+ serverPublicKey: 'BASE64_SERVER_PUBLIC_KEY',
131
+ transport: 'auto', // default — QUIC first, then WS
132
+ });
133
+ ```
134
+
92
135
  ### VPN Server
93
136
 
94
137
  ```typescript
@@ -100,10 +143,9 @@ const server = new VpnServer({
100
143
 
101
144
  // Generate a Noise keypair first
102
145
  await server.start();
103
- // If you don't have keys yet:
104
146
  const keypair = await server.generateKeypair();
105
147
 
106
- // Start the VPN listener (or pass config to start() directly)
148
+ // Start the VPN listener
107
149
  await server.start({
108
150
  listenAddr: '0.0.0.0:443',
109
151
  privateKey: keypair.privateKey,
@@ -112,6 +154,12 @@ await server.start({
112
154
  dns: ['1.1.1.1'],
113
155
  mtu: 1420,
114
156
  enableNat: true,
157
+ // Transport mode: 'websocket', 'quic', or 'both' (default)
158
+ transportMode: 'both',
159
+ // Optional: separate QUIC listen address
160
+ quicListenAddr: '0.0.0.0:4433',
161
+ // Optional: QUIC idle timeout
162
+ quicIdleTimeoutSecs: 30,
115
163
  // Optional: default rate limit for all new clients
116
164
  defaultRateLimitBytesPerSec: 10_000_000, // 10 MB/s
117
165
  defaultBurstBytes: 20_000_000, // 20 MB burst
@@ -247,11 +295,66 @@ Both `VpnClient` and `VpnServer` extend `EventEmitter`:
247
295
  ```typescript
248
296
  client.on('exit', ({ code, signal }) => { /* daemon exited */ });
249
297
  client.on('reconnected', () => { /* socket reconnected */ });
298
+ client.on('status', (status) => { /* IVpnStatus update */ });
299
+ client.on('error', (error) => { /* error from daemon */ });
250
300
 
251
301
  server.on('client-connected', (info) => { /* IVpnClientInfo */ });
252
302
  server.on('client-disconnected', ({ clientId, reason }) => { /* ... */ });
303
+ server.on('started', () => { /* server listener started */ });
304
+ server.on('stopped', () => { /* server listener stopped */ });
253
305
  ```
254
306
 
307
+ ## 🌐 Transport Modes
308
+
309
+ smartvpn supports two transport protocols through a unified transport abstraction layer. Both use the same encryption, framing, and QoS pipeline — the transport is swappable without changing any application logic.
310
+
311
+ ### WebSocket (default)
312
+
313
+ - Works through Cloudflare, reverse proxies, and HTTP load balancers
314
+ - Reliable delivery only (no datagram support)
315
+ - URL format: `wss://host/path` or `ws://host:port/path`
316
+
317
+ ### QUIC
318
+
319
+ - Lower latency, built-in multiplexing, 0-RTT connection establishment
320
+ - Supports **unreliable datagrams** for IP packets (with automatic fallback to reliable if oversized)
321
+ - Certificate hash pinning — no CA chain needed, WireGuard-style trust
322
+ - URL format: `host:port`
323
+ - ALPN protocol: `smartvpn`
324
+
325
+ ### Auto-Transport (Recommended)
326
+
327
+ The default `transport: 'auto'` mode gives you the best of both worlds:
328
+
329
+ 1. Extract `host:port` from the WebSocket URL
330
+ 2. Attempt QUIC connection (3-second timeout)
331
+ 3. If QUIC fails or times out → fall back to WebSocket
332
+ 4. Completely transparent to the application
333
+
334
+ ```typescript
335
+ await client.connect({
336
+ serverUrl: 'wss://vpn.example.com/tunnel',
337
+ serverPublicKey: '...',
338
+ transport: 'auto', // default — QUIC first, WS fallback
339
+ });
340
+ ```
341
+
342
+ ### Server Dual-Mode
343
+
344
+ The server can listen on both transports simultaneously:
345
+
346
+ ```typescript
347
+ await server.start({
348
+ listenAddr: '0.0.0.0:443', // WebSocket listener
349
+ quicListenAddr: '0.0.0.0:4433', // QUIC listener (optional, defaults to listenAddr)
350
+ transportMode: 'both', // 'websocket' | 'quic' | 'both' (default)
351
+ quicIdleTimeoutSecs: 30, // QUIC connection idle timeout
352
+ // ... other config
353
+ });
354
+ ```
355
+
356
+ When using `'both'` mode, the server logs the QUIC certificate hash on startup — share this with clients for cert pinning.
357
+
255
358
  ## 📊 QoS System
256
359
 
257
360
  The Rust daemon includes a full QoS stack that operates on decrypted IP packets:
@@ -337,9 +440,26 @@ Post-handshake, all IP packets are encrypted with **XChaCha20-Poly1305**:
337
440
  - 16-byte authentication tags
338
441
  - Wire format: `[nonce:24B][ciphertext:var][tag:16B]`
339
442
 
443
+ ### QUIC Certificate Pinning
444
+
445
+ When using QUIC transport, the server generates a self-signed TLS certificate (or uses a configured PEM). Instead of relying on a CA chain, clients pin the server's certificate by its **SHA-256 hash** (base64-encoded) — a WireGuard-inspired trust model:
446
+
447
+ ```typescript
448
+ // Server logs the cert hash on startup:
449
+ // "QUIC cert hash: <BASE64_HASH>"
450
+
451
+ // Client pins it:
452
+ await client.connect({
453
+ serverUrl: 'vpn.example.com:443',
454
+ transport: 'quic',
455
+ serverCertHash: '<BASE64_HASH>',
456
+ serverPublicKey: '...',
457
+ });
458
+ ```
459
+
340
460
  ## 📦 Binary Protocol
341
461
 
342
- Inside the WebSocket tunnel, packets use a simple binary framing:
462
+ Inside the tunnel (both WebSocket and QUIC reliable channels), packets use a simple binary framing:
343
463
 
344
464
  ```
345
465
  ┌──────────┬──────────┬────────────────────┐
@@ -359,6 +479,8 @@ Inside the WebSocket tunnel, packets use a simple binary framing:
359
479
  | `SessionResumeErr` | `0x32` | Resume rejected |
360
480
  | `Disconnect` | `0x3F` | Graceful disconnect |
361
481
 
482
+ When QUIC datagrams are available, IP packets can optionally be sent via the unreliable datagram channel for lower latency. Packets that exceed the max datagram size automatically fall back to the reliable stream.
483
+
362
484
  ## 🛠️ Rust Daemon CLI
363
485
 
364
486
  ```bash
@@ -385,12 +507,12 @@ pnpm build
385
507
  # Build Rust only (debug)
386
508
  cd rust && cargo build
387
509
 
388
- # Run all tests (71 Rust + 32 TypeScript)
510
+ # Run all tests (77 Rust + 59 TypeScript)
389
511
  cd rust && cargo test
390
512
  pnpm test
391
513
  ```
392
514
 
393
- ## TypeScript Interfaces
515
+ ## 📘 TypeScript Interfaces
394
516
 
395
517
  <details>
396
518
  <summary>Click to expand full type definitions</summary>
@@ -410,8 +532,10 @@ type TVpnTransportOptions =
410
532
 
411
533
  // Client config
412
534
  interface IVpnClientConfig {
413
- serverUrl: string;
414
- serverPublicKey: string;
535
+ serverUrl: string; // WS: 'wss://host/path' | QUIC: 'host:port'
536
+ serverPublicKey: string; // Base64-encoded Noise static key
537
+ transport?: 'auto' | 'websocket' | 'quic'; // Default: 'auto'
538
+ serverCertHash?: string; // SHA-256 cert hash (base64) for QUIC pinning
415
539
  dns?: string[];
416
540
  mtu?: number;
417
541
  keepaliveIntervalSecs?: number;
@@ -429,6 +553,9 @@ interface IVpnServerConfig {
429
553
  mtu?: number;
430
554
  keepaliveIntervalSecs?: number;
431
555
  enableNat?: boolean;
556
+ transportMode?: 'websocket' | 'quic' | 'both'; // Default: 'both'
557
+ quicListenAddr?: string; // Separate QUIC bind address
558
+ quicIdleTimeoutSecs?: number; // QUIC idle timeout (default: 30)
432
559
  defaultRateLimitBytesPerSec?: number;
433
560
  defaultBurstBytes?: number;
434
561
  }
@@ -537,7 +664,7 @@ Use of these trademarks must comply with Task Venture Capital GmbH's Trademark G
537
664
 
538
665
  ### Company Information
539
666
 
540
- Task Venture Capital GmbH
667
+ Task Venture Capital GmbH
541
668
  Registered at District Court Bremen HRB 35230 HB, Germany
542
669
 
543
670
  For any legal inquiries or further information, please contact us via email at hello@task.vc.
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartvpn',
6
- version: '1.3.0',
6
+ version: '1.4.0',
7
7
  description: 'A VPN solution with TypeScript control plane and Rust data plane daemon'
8
8
  }
@@ -15,8 +15,11 @@ export class VpnConfig {
15
15
  if (!config.serverUrl) {
16
16
  throw new Error('VpnConfig: serverUrl is required');
17
17
  }
18
- if (!config.serverUrl.startsWith('wss://') && !config.serverUrl.startsWith('ws://')) {
19
- throw new Error('VpnConfig: serverUrl must start with wss:// or ws://');
18
+ // For QUIC-only transport, serverUrl is a host:port address; for WebSocket/auto it must be ws:// or wss://
19
+ if (config.transport !== 'quic') {
20
+ if (!config.serverUrl.startsWith('wss://') && !config.serverUrl.startsWith('ws://')) {
21
+ throw new Error('VpnConfig: serverUrl must start with wss:// or ws:// (for WebSocket transport)');
22
+ }
20
23
  }
21
24
  if (!config.serverPublicKey) {
22
25
  throw new Error('VpnConfig: serverPublicKey is required');
@@ -32,6 +32,10 @@ export interface IVpnClientConfig {
32
32
  mtu?: number;
33
33
  /** Keepalive interval in seconds (default: 30) */
34
34
  keepaliveIntervalSecs?: number;
35
+ /** Transport protocol: 'auto' (default, tries QUIC then WS), 'websocket', or 'quic' */
36
+ transport?: 'auto' | 'websocket' | 'quic';
37
+ /** For QUIC: SHA-256 hash of server certificate (base64) for cert pinning */
38
+ serverCertHash?: string;
35
39
  }
36
40
 
37
41
  export interface IVpnClientOptions {
@@ -68,6 +72,12 @@ export interface IVpnServerConfig {
68
72
  defaultRateLimitBytesPerSec?: number;
69
73
  /** Default burst size for new clients (bytes). Omit for unlimited. */
70
74
  defaultBurstBytes?: number;
75
+ /** Transport mode: 'both' (default, WS+QUIC), 'websocket', or 'quic' */
76
+ transportMode?: 'websocket' | 'quic' | 'both';
77
+ /** QUIC listen address (host:port). Defaults to listenAddr. */
78
+ quicListenAddr?: string;
79
+ /** QUIC idle timeout in seconds (default: 30) */
80
+ quicIdleTimeoutSecs?: number;
71
81
  }
72
82
 
73
83
  export interface IVpnServerOptions {