@push.rocks/smartvpn 1.7.0 → 1.9.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.
package/readme.md CHANGED
@@ -1,893 +1,394 @@
1
1
  # @push.rocks/smartvpn
2
2
 
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.
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 NK** handshake + **XChaCha20-Poly1305** encryption
5
+ 🔐 **Noise IK** mutual authentication — per-client X25519 keypairs, server-side registry
6
6
  🚀 **Triple transport**: WebSocket (Cloudflare-friendly), raw **QUIC** (datagrams), and **WireGuard** (standard protocol)
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
- 🛡️ **WireGuard mode**: full userspace WireGuard via `boringtun` generate `.conf` files, manage peers live
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
+ 📊 **Adaptive QoS**: per-client rate limiting, priority queues, connection quality tracking
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
11
12
 
12
13
  ## Issue Reporting and Security
13
14
 
14
15
  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.
15
16
 
16
- ## Install
17
+ ## Install 📦
17
18
 
18
19
  ```bash
19
20
  pnpm install @push.rocks/smartvpn
21
+ # or
22
+ npm install @push.rocks/smartvpn
20
23
  ```
21
24
 
22
- ## 🏗️ Architecture
25
+ The package ships with pre-compiled Rust binaries for **linux/amd64** and **linux/arm64**. No Rust toolchain required at runtime.
26
+
27
+ ## Architecture 🏗️
23
28
 
24
29
  ```
25
- TypeScript (control plane) Rust (data plane)
26
- ┌──────────────────────────┐ ┌────────────────────────────────────┐
27
- VpnClient / VpnServer smartvpn_daemon │
28
- └─ VpnBridge │──stdio/──▶├─ management (JSON IPC)
29
- └─ RustBridge socket ├─ transport_trait (abstraction)
30
- (smartrust) │ ├─ transport (WebSocket/TLS)
31
- │ │ │ └─ quic_transport (QUIC/UDP)
32
- WgConfigGenerator ├─ wireguard (boringtun WG)
33
- │ └─ .conf file output │ │ ├─ crypto (Noise NK + XCha20) │
34
- └──────────────────────────┘ │ ├─ codec (binary framing) │
35
- │ ├─ keepalive (adaptive state FSM) │
36
- │ ├─ telemetry (RTT/jitter/loss) │
37
- │ ├─ qos (classify + priority Q) │
38
- │ ├─ ratelimit (token bucket) │
39
- │ ├─ mtu (overhead calc + ICMP) │
40
- │ ├─ tunnel (TUN device) │
41
- │ ├─ network (NAT/IP pool) │
42
- │ └─ reconnect (exp. backoff) │
43
- └────────────────────────────────────┘
30
+ ┌──────────────────────────────┐ JSON-lines IPC ┌───────────────────────────────┐
31
+ │ TypeScript Control Plane │ ◄─────────────────────► │ Rust Data Plane Daemon │
32
+ │ stdio or Unix sock
33
+ VpnServer / VpnClient Noise IK handshake
34
+ Typed IPC commands XChaCha20-Poly1305
35
+ Config validation WS + QUIC + WireGuard
36
+ Hub: client management │TUN device, IP pool, NAT
37
+ WireGuard .conf generation Rate limiting, ACLs, QoS
38
+ └──────────────────────────────┘ └───────────────────────────────┘
44
39
  ```
45
40
 
46
- **Key design decisions:**
47
-
48
- | Decision | Choice | Why |
49
- |----------|--------|-----|
50
- | Transport | WebSocket + QUIC + WireGuard | WS works through Cloudflare; QUIC gives low latency + datagrams; WG for standard protocol interop |
51
- | Auto-transport | QUIC first, WS fallback | Best performance when QUIC is available, graceful degradation when it's not |
52
- | WireGuard | Userspace via `boringtun` | No kernel module needed, runs on any platform, full peer management via IPC |
53
- | Encryption | Noise NK + XChaCha20-Poly1305 | Strong forward secrecy, large nonce space (no counter sync needed) |
54
- | QUIC auth | Certificate hash pinning | WireGuard-style trust model — no CA needed, just pin the server cert hash |
55
- | Keepalive | Adaptive app-level pings | Cloudflare drops WS pings; interval adapts to link health (10–60s) |
56
- | QoS | Packet classification + priority queues | DNS/SSH/ICMP always drain first; bulk flows get deprioritized |
57
- | Rate limiting | Per-client token bucket | Byte-granular, dynamically reconfigurable via IPC |
58
- | IPC | JSON lines over stdio / Unix socket | `stdio` for dev, `socket` for production (daemon stays alive) |
59
- | Binary protocol | `[type:1B][length:4B][payload:NB]` | Minimal overhead, easy to parse at wire speed |
41
+ **Split-plane design** — TypeScript handles orchestration, config, and DX; Rust handles every hot-path byte with zero-copy async I/O (tokio, mimalloc).
60
42
 
61
- ## 🚀 Quick Start
43
+ ## Quick Start 🚀
62
44
 
63
- ### VPN Client
45
+ ### 1. Start a VPN Server (Hub)
64
46
 
65
47
  ```typescript
66
- import { VpnClient } from '@push.rocks/smartvpn';
67
-
68
- const client = new VpnClient({
69
- transport: { transport: 'stdio' },
70
- });
71
-
72
- await client.start();
48
+ import { VpnServer } from '@push.rocks/smartvpn';
73
49
 
74
- const { assignedIp } = await client.connect({
75
- serverUrl: 'wss://vpn.example.com/tunnel',
76
- serverPublicKey: 'BASE64_SERVER_PUBLIC_KEY',
50
+ const server = new VpnServer({ transport: { transport: 'stdio' } });
51
+ await server.start({
52
+ listenAddr: '0.0.0.0:443',
53
+ privateKey: '<server-noise-private-key-base64>',
54
+ publicKey: '<server-noise-public-key-base64>',
55
+ subnet: '10.8.0.0/24',
56
+ transportMode: 'both', // WebSocket + QUIC simultaneously
57
+ enableNat: true,
77
58
  dns: ['1.1.1.1', '8.8.8.8'],
78
- mtu: 1420,
79
- keepaliveIntervalSecs: 30,
80
59
  });
60
+ ```
81
61
 
82
- console.log(`Connected! Assigned IP: ${assignedIp}`);
83
-
84
- // Connection quality (adaptive keepalive + telemetry)
85
- const quality = await client.getConnectionQuality();
86
- console.log(quality);
87
- // {
88
- // srttMs: 42.5, jitterMs: 3.2, minRttMs: 38.0, maxRttMs: 67.0,
89
- // lossRatio: 0.0, consecutiveTimeouts: 0,
90
- // linkHealth: 'healthy', currentKeepaliveIntervalSecs: 60
91
- // }
92
-
93
- // MTU info
94
- const mtu = await client.getMtuInfo();
95
- console.log(mtu);
96
- // { tunMtu: 1420, effectiveMtu: 1421, linkMtu: 1500, overheadBytes: 79, ... }
62
+ ### 2. Create a Client (One Call = Everything)
97
63
 
98
- // Traffic stats (includes quality snapshot)
99
- const stats = await client.getStatistics();
64
+ ```typescript
65
+ const bundle = await server.createClient({
66
+ clientId: 'alice-laptop',
67
+ tags: ['engineering'],
68
+ security: {
69
+ destinationAllowList: ['10.0.0.0/8'], // can only reach internal network
70
+ destinationBlockList: ['10.0.0.99'], // except this host
71
+ rateLimit: { bytesPerSec: 10_000_000, burstBytes: 20_000_000 },
72
+ },
73
+ });
100
74
 
101
- await client.disconnect();
102
- client.stop();
75
+ // bundle.smartvpnConfig → typed IVpnClientConfig, ready to use
76
+ // bundle.wireguardConfig → standard WireGuard .conf string
77
+ // bundle.secrets → { noisePrivateKey, wgPrivateKey } — shown ONCE
103
78
  ```
104
79
 
105
- ### VPN Client with QUIC
80
+ ### 3. Connect a Client
106
81
 
107
82
  ```typescript
108
83
  import { VpnClient } from '@push.rocks/smartvpn';
109
84
 
110
- // Explicit QUIC serverUrl is host:port, pinned by cert hash
111
- const quicClient = new VpnClient({
112
- transport: { transport: 'stdio' },
113
- });
85
+ const client = new VpnClient({ transport: { transport: 'stdio' } });
86
+ await client.start();
114
87
 
115
- await quicClient.start();
88
+ const { assignedIp } = await client.connect(bundle.smartvpnConfig);
89
+ console.log(`Connected! VPN IP: ${assignedIp}`);
90
+ ```
116
91
 
117
- const { assignedIp } = await quicClient.connect({
118
- serverUrl: 'vpn.example.com:443',
119
- serverPublicKey: 'BASE64_SERVER_PUBLIC_KEY',
120
- transport: 'quic',
121
- serverCertHash: 'BASE64_SHA256_CERT_HASH', // printed by server on startup
122
- });
92
+ ## Features
123
93
 
124
- // Or use auto-transport: tries QUIC first (3s timeout), falls back to WS
125
- const autoClient = new VpnClient({
126
- transport: { transport: 'stdio' },
127
- });
94
+ ### 🔐 Enterprise Authentication (Noise IK)
128
95
 
129
- await autoClient.start();
96
+ 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.
130
97
 
131
- await autoClient.connect({
132
- serverUrl: 'wss://vpn.example.com/tunnel', // WS URL host:port extracted for QUIC attempt
133
- serverPublicKey: 'BASE64_SERVER_PUBLIC_KEY',
134
- transport: 'auto', // default — QUIC first, then WS
135
- });
136
- ```
137
-
138
- ### VPN Client with WireGuard
98
+ - Per-client X25519 keypair generated server-side
99
+ - Client registry with enable/disable, expiry, tags
100
+ - Key rotation with `rotateClientKey()` — generates new keys, returns fresh config bundle, disconnects old session
139
101
 
140
- ```typescript
141
- import { VpnClient } from '@push.rocks/smartvpn';
102
+ ### 🌐 Triple Transport
142
103
 
143
- const wgClient = new VpnClient({
144
- transport: { transport: 'stdio' },
145
- });
104
+ | Transport | Protocol | Best For |
105
+ |-----------|----------|----------|
106
+ | **WebSocket** | TLS over TCP | Firewall-friendly, Cloudflare compatible |
107
+ | **QUIC** | UDP (via quinn) | Low latency, datagram support for IP packets |
108
+ | **WireGuard** | UDP (via boringtun) | Standard WG clients (iOS, Android, wg-quick) |
146
109
 
147
- await wgClient.start();
148
-
149
- const { assignedIp } = await wgClient.connect({
150
- serverPublicKey: 'BASE64_SERVER_WG_PUBLIC_KEY',
151
- serverUrl: '', // not used for WireGuard
152
- transport: 'wireguard',
153
- wgPrivateKey: 'BASE64_CLIENT_PRIVATE_KEY',
154
- wgAddress: '10.8.0.2',
155
- wgAddressPrefix: 24,
156
- wgEndpoint: 'vpn.example.com:51820',
157
- wgAllowedIps: ['0.0.0.0/0'], // route all traffic
158
- wgPersistentKeepalive: 25,
159
- wgPresharedKey: 'OPTIONAL_PSK', // optional extra layer
160
- dns: ['1.1.1.1'],
161
- mtu: 1420,
162
- });
110
+ 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).
163
111
 
164
- console.log(`WireGuard connected! IP: ${assignedIp}`);
165
- await wgClient.disconnect();
166
- wgClient.stop();
167
- ```
112
+ ### 🛡️ ACL Engine (SmartProxy-Aligned)
168
113
 
169
- ### VPN Server
114
+ Security policies per client, using the same `ipAllowList` / `ipBlockList` naming convention as `@push.rocks/smartproxy`:
170
115
 
171
116
  ```typescript
172
- import { VpnServer } from '@push.rocks/smartvpn';
117
+ security: {
118
+ ipAllowList: ['192.168.1.0/24'], // source IPs allowed to connect
119
+ ipBlockList: ['192.168.1.100'], // deny overrides allow
120
+ destinationAllowList: ['10.0.0.0/8'], // VPN destinations permitted
121
+ destinationBlockList: ['10.0.0.99'], // deny overrides allow
122
+ maxConnections: 5,
123
+ rateLimit: { bytesPerSec: 1_000_000, burstBytes: 2_000_000 },
124
+ }
125
+ ```
173
126
 
174
- const server = new VpnServer({
175
- transport: { transport: 'stdio' },
176
- });
127
+ Supports exact IPs, CIDR, wildcards (`192.168.1.*`), and ranges (`1.1.1.1-1.1.1.100`).
177
128
 
178
- // Generate a Noise keypair first
179
- await server.start();
180
- const keypair = await server.generateKeypair();
129
+ ### 🔀 PROXY Protocol v2
181
130
 
182
- // Start the VPN listener
131
+ 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.
132
+
133
+ ```typescript
183
134
  await server.start({
184
- listenAddr: '0.0.0.0:443',
185
- privateKey: keypair.privateKey,
186
- publicKey: keypair.publicKey,
187
- subnet: '10.8.0.0/24',
188
- dns: ['1.1.1.1'],
189
- mtu: 1420,
190
- enableNat: true,
191
- // Transport mode: 'websocket', 'quic', 'both', or 'wireguard'
192
- transportMode: 'both',
193
- // Optional: separate QUIC listen address
194
- quicListenAddr: '0.0.0.0:4433',
195
- // Optional: QUIC idle timeout
196
- quicIdleTimeoutSecs: 30,
197
- // Optional: default rate limit for all new clients
198
- defaultRateLimitBytesPerSec: 10_000_000, // 10 MB/s
199
- defaultBurstBytes: 20_000_000, // 20 MB burst
135
+ // ... other config ...
136
+ proxyProtocol: true, // parse PP v2 headers on WS connections
137
+ connectionIpBlockList: ['198.51.100.0/24'], // server-wide block list (pre-handshake)
200
138
  });
139
+ ```
201
140
 
202
- // List connected clients
203
- const clients = await server.listClients();
141
+ **Two-phase ACL with real IPs:**
204
142
 
205
- // Per-client rate limiting (live, no reconnect needed)
206
- await server.setClientRateLimit('client-id', 5_000_000, 10_000_000);
207
- await server.removeClientRateLimit('client-id'); // unlimited
143
+ | Phase | When | What Happens |
144
+ |-------|------|-------------|
145
+ | **Pre-handshake** | After TCP accept | Server-level `connectionIpBlockList` rejects known-bad IPs — zero crypto cost |
146
+ | **Post-handshake** | After Noise IK identifies client | Per-client `ipAllowList` / `ipBlockList` checked against real source IP |
208
147
 
209
- // Per-client telemetry
210
- const telemetry = await server.getClientTelemetry('client-id');
211
- console.log(telemetry);
212
- // {
213
- // clientId, assignedIp, lastKeepaliveAt, keepalivesReceived,
214
- // packetsDropped, bytesDropped, bytesReceived, bytesSent,
215
- // rateLimitBytesPerSec, burstBytes
216
- // }
148
+ - Parses the PP v2 binary header from raw TCP before WebSocket upgrade
149
+ - 5-second timeout protects against stalling attacks
150
+ - LOCAL command (proxy health checks) handled gracefully
151
+ - IPv4 and IPv6 addresses supported
152
+ - `remoteAddr` field on `IVpnClientInfo` exposes the real client IP for monitoring
153
+ - **Security**: must be `false` (default) when accepting direct connections — only enable behind a trusted proxy
217
154
 
218
- // Kick a client
219
- await server.disconnectClient('client-id');
155
+ ### 📊 Telemetry & QoS
220
156
 
221
- await server.stopServer();
222
- server.stop();
223
- ```
157
+ - **Connection quality**: Smoothed RTT, jitter, min/max RTT, loss ratio, link health (`healthy` / `degraded` / `critical`)
158
+ - **Adaptive keepalives**: Interval adjusts based on link health (60s → 30s → 10s)
159
+ - **Per-client rate limiting**: Token bucket with configurable bytes/sec and burst
160
+ - **Dead-peer detection**: 180s inactivity timeout
161
+ - **MTU management**: Automatic overhead calculation (IP+TCP+WS+Noise = 79 bytes)
224
162
 
225
- ### WireGuard Server Mode
163
+ ### 🔄 Hub Client Management
226
164
 
227
- ```typescript
228
- import { VpnServer } from '@push.rocks/smartvpn';
165
+ The server acts as a **hub** — one API to manage all clients:
229
166
 
230
- const wgServer = new VpnServer({
231
- transport: { transport: 'stdio' },
232
- });
167
+ ```typescript
168
+ // Create (generates keys, assigns IP, returns config bundle)
169
+ const bundle = await server.createClient({ clientId: 'bob-phone' });
233
170
 
234
- // Generate a WireGuard X25519 keypair
235
- await wgServer.start();
236
- const keypair = await wgServer.generateWgKeypair();
237
- console.log(`Server public key: ${keypair.publicKey}`);
171
+ // Read
172
+ const entry = await server.getClient('bob-phone');
173
+ const all = await server.listRegisteredClients();
238
174
 
239
- // Start in WireGuard mode
240
- await wgServer.start({
241
- listenAddr: '0.0.0.0:51820',
242
- privateKey: keypair.privateKey,
243
- publicKey: keypair.publicKey,
244
- subnet: '10.8.0.0/24',
245
- transportMode: 'wireguard',
246
- wgListenPort: 51820,
247
- wgPeers: [
248
- {
249
- publicKey: 'CLIENT_PUBLIC_KEY_BASE64',
250
- allowedIps: ['10.8.0.2/32'],
251
- persistentKeepalive: 25,
252
- },
253
- ],
254
- enableNat: true,
255
- dns: ['1.1.1.1'],
256
- mtu: 1420,
175
+ // Update (ACLs, tags, description, rate limits...)
176
+ await server.updateClient('bob-phone', {
177
+ security: { destinationAllowList: ['0.0.0.0/0'] },
178
+ tags: ['mobile', 'field-ops'],
257
179
  });
258
180
 
259
- // Live peer management — add/remove peers without restart
260
- await wgServer.addWgPeer({
261
- publicKey: 'NEW_CLIENT_PUBLIC_KEY',
262
- allowedIps: ['10.8.0.3/32'],
263
- persistentKeepalive: 25,
264
- });
181
+ // Enable / Disable
182
+ await server.disableClient('bob-phone'); // disconnects + blocks reconnection
183
+ await server.enableClient('bob-phone');
265
184
 
266
- // List peers with live stats
267
- const peers = await wgServer.listWgPeers();
268
- for (const peer of peers) {
269
- console.log(`${peer.publicKey}: ↑${peer.bytesSent} ↓${peer.bytesReceived}`);
270
- }
185
+ // Key rotation
186
+ const newBundle = await server.rotateClientKey('bob-phone');
271
187
 
272
- // Remove a peer by public key
273
- await wgServer.removeWgPeer('CLIENT_PUBLIC_KEY_BASE64');
188
+ // Export config (without secrets)
189
+ const wgConf = await server.exportClientConfig('bob-phone', 'wireguard');
274
190
 
275
- await wgServer.stopServer();
276
- wgServer.stop();
191
+ // Remove
192
+ await server.removeClient('bob-phone');
277
193
  ```
278
194
 
279
- ### Generating WireGuard .conf Files
195
+ ### 📝 WireGuard Config Generation
280
196
 
281
- The `WgConfigGenerator` creates standard WireGuard `.conf` files compatible with `wg-quick`, iOS/Android apps, and all standard WireGuard clients:
197
+ Generate standard `.conf` files for any WireGuard client:
282
198
 
283
199
  ```typescript
284
200
  import { WgConfigGenerator } from '@push.rocks/smartvpn';
285
201
 
286
- // Client config (for wg-quick or mobile apps)
287
- const clientConf = WgConfigGenerator.generateClientConfig({
288
- privateKey: 'CLIENT_PRIVATE_KEY_BASE64',
202
+ const conf = WgConfigGenerator.generateClientConfig({
203
+ privateKey: '<client-wg-private-key>',
289
204
  address: '10.8.0.2/24',
290
- dns: ['1.1.1.1', '8.8.8.8'],
291
- mtu: 1420,
205
+ dns: ['1.1.1.1'],
292
206
  peer: {
293
- publicKey: 'SERVER_PUBLIC_KEY_BASE64',
207
+ publicKey: '<server-wg-public-key>',
294
208
  endpoint: 'vpn.example.com:51820',
295
- allowedIps: ['0.0.0.0/0', '::/0'],
209
+ allowedIps: ['0.0.0.0/0'],
296
210
  persistentKeepalive: 25,
297
- presharedKey: 'OPTIONAL_PSK_BASE64',
298
- },
299
- });
300
-
301
- // Server config (for wg-quick)
302
- const serverConf = WgConfigGenerator.generateServerConfig({
303
- privateKey: 'SERVER_PRIVATE_KEY_BASE64',
304
- address: '10.8.0.1/24',
305
- listenPort: 51820,
306
- dns: ['1.1.1.1'],
307
- mtu: 1420,
308
- enableNat: true,
309
- natInterface: 'eth0', // auto-detected if omitted
310
- peers: [
311
- {
312
- publicKey: 'CLIENT_PUBLIC_KEY_BASE64',
313
- allowedIps: ['10.8.0.2/32'],
314
- persistentKeepalive: 25,
315
- },
316
- ],
317
- });
318
-
319
- // Write to disk
320
- import * as fs from 'fs';
321
- fs.writeFileSync('/etc/wireguard/wg0.conf', serverConf);
322
- ```
323
-
324
- <details>
325
- <summary>Example output: client .conf</summary>
326
-
327
- ```ini
328
- [Interface]
329
- PrivateKey = CLIENT_PRIVATE_KEY_BASE64
330
- Address = 10.8.0.2/24
331
- DNS = 1.1.1.1, 8.8.8.8
332
- MTU = 1420
333
-
334
- [Peer]
335
- PublicKey = SERVER_PUBLIC_KEY_BASE64
336
- PresharedKey = OPTIONAL_PSK_BASE64
337
- Endpoint = vpn.example.com:51820
338
- AllowedIPs = 0.0.0.0/0, ::/0
339
- PersistentKeepalive = 25
340
- ```
341
-
342
- </details>
343
-
344
- ### Production: Socket Transport
345
-
346
- In production, the daemon runs as a system service and you connect over a Unix socket:
347
-
348
- ```typescript
349
- const client = new VpnClient({
350
- transport: {
351
- transport: 'socket',
352
- socketPath: '/var/run/smartvpn.sock',
353
- autoReconnect: true,
354
- reconnectBaseDelayMs: 100,
355
- reconnectMaxDelayMs: 30000,
356
- maxReconnectAttempts: 10,
357
211
  },
358
212
  });
359
-
360
- await client.start(); // connects to existing daemon (does not spawn)
213
+ // → standard WireGuard .conf compatible with wg-quick, iOS, Android
361
214
  ```
362
215
 
363
- When using socket transport, `client.stop()` closes the socket but **does not kill the daemon** — exactly what you want in production.
364
-
365
- ## 📋 API Reference
366
-
367
- ### `VpnClient`
368
-
369
- | Method | Returns | Description |
370
- |--------|---------|-------------|
371
- | `start()` | `Promise<boolean>` | Start the daemon bridge (spawn or connect) |
372
- | `connect(config?)` | `Promise<{ assignedIp }>` | Connect to VPN server (WS, QUIC, or WireGuard) |
373
- | `disconnect()` | `Promise<void>` | Disconnect from VPN |
374
- | `getStatus()` | `Promise<IVpnStatus>` | Current connection state |
375
- | `getStatistics()` | `Promise<IVpnStatistics>` | Traffic stats + connection quality |
376
- | `getConnectionQuality()` | `Promise<IVpnConnectionQuality>` | RTT, jitter, loss, link health |
377
- | `getMtuInfo()` | `Promise<IVpnMtuInfo>` | MTU info and overhead breakdown |
378
- | `stop()` | `void` | Kill/close the daemon bridge |
379
- | `running` | `boolean` | Whether bridge is active |
380
-
381
- ### `VpnServer`
382
-
383
- | Method | Returns | Description |
384
- |--------|---------|-------------|
385
- | `start(config?)` | `Promise<void>` | Start daemon + VPN server |
386
- | `stopServer()` | `Promise<void>` | Stop the VPN server |
387
- | `getStatus()` | `Promise<IVpnStatus>` | Server connection state |
388
- | `getStatistics()` | `Promise<IVpnServerStatistics>` | Server stats (includes client counts) |
389
- | `listClients()` | `Promise<IVpnClientInfo[]>` | Connected clients with QoS stats |
390
- | `disconnectClient(id)` | `Promise<void>` | Kick a client |
391
- | `generateKeypair()` | `Promise<IVpnKeypair>` | Generate Noise NK keypair |
392
- | `setClientRateLimit(id, rate, burst)` | `Promise<void>` | Set per-client rate limit (bytes/sec) |
393
- | `removeClientRateLimit(id)` | `Promise<void>` | Remove rate limit (unlimited) |
394
- | `getClientTelemetry(id)` | `Promise<IVpnClientTelemetry>` | Per-client telemetry + drop stats |
395
- | `generateWgKeypair()` | `Promise<IVpnKeypair>` | Generate WireGuard X25519 keypair |
396
- | `addWgPeer(peer)` | `Promise<void>` | Add a WireGuard peer at runtime |
397
- | `removeWgPeer(publicKey)` | `Promise<void>` | Remove a WireGuard peer by key |
398
- | `listWgPeers()` | `Promise<IWgPeerInfo[]>` | List WG peers with traffic stats |
399
- | `stop()` | `void` | Kill/close the daemon bridge |
400
-
401
- ### `VpnConfig`
402
-
403
- Static utility class for config validation and file I/O:
404
-
405
- ```typescript
406
- import { VpnConfig } from '@push.rocks/smartvpn';
407
-
408
- // Validate (throws on invalid)
409
- VpnConfig.validateClientConfig(config);
410
- VpnConfig.validateServerConfig(config);
411
-
412
- // Load/save JSON configs
413
- const config = await VpnConfig.loadFromFile<IVpnClientConfig>('/etc/smartvpn/client.json');
414
- await VpnConfig.saveToFile('/etc/smartvpn/client.json', config);
415
- ```
416
-
417
- Validation covers both smartvpn-native configs and WireGuard configs — base64 key format, CIDR ranges, port ranges, and required fields are all checked.
418
-
419
- ### `WgConfigGenerator`
420
-
421
- Static generator for standard WireGuard `.conf` files:
422
-
423
- | Method | Returns | Description |
424
- |--------|---------|-------------|
425
- | `generateClientConfig(opts)` | `string` | Generate a `wg-quick` compatible client `.conf` |
426
- | `generateServerConfig(opts)` | `string` | Generate a `wg-quick` compatible server `.conf` with NAT rules |
427
-
428
- Output is compatible with `wg-quick`, WireGuard iOS/Android apps, and any standard WireGuard implementation.
429
-
430
- ### `VpnInstaller`
431
-
432
- Generate system service units for the daemon:
216
+ ### 🖥️ System Service Installation
433
217
 
434
218
  ```typescript
435
219
  import { VpnInstaller } from '@push.rocks/smartvpn';
436
220
 
437
- const platform = VpnInstaller.detectPlatform(); // 'linux' | 'macos' | 'windows' | 'unknown'
438
-
439
- // Linux (systemd)
440
- const unit = VpnInstaller.generateSystemdUnit({
441
- binaryPath: '/usr/local/bin/smartvpn_daemon',
442
- socketPath: '/var/run/smartvpn.sock',
221
+ const unit = VpnInstaller.generateServiceUnit({
443
222
  mode: 'server',
223
+ configPath: '/etc/smartvpn/server.json',
444
224
  });
445
-
446
- // macOS (launchd)
447
- const plist = VpnInstaller.generateLaunchdPlist({
448
- binaryPath: '/usr/local/bin/smartvpn_daemon',
449
- socketPath: '/var/run/smartvpn.sock',
450
- mode: 'client',
451
- });
452
-
453
- // Auto-detect platform
454
- const serviceUnit = VpnInstaller.generateServiceUnit({
455
- binaryPath: '/usr/local/bin/smartvpn_daemon',
456
- socketPath: '/var/run/smartvpn.sock',
457
- mode: 'server',
458
- });
459
- ```
460
-
461
- ### Events
462
-
463
- Both `VpnClient` and `VpnServer` extend `EventEmitter`:
464
-
465
- ```typescript
466
- client.on('exit', ({ code, signal }) => { /* daemon exited */ });
467
- client.on('reconnected', () => { /* socket reconnected */ });
468
- client.on('status', (status) => { /* IVpnStatus update */ });
469
- client.on('error', (error) => { /* error from daemon */ });
470
-
471
- server.on('client-connected', (info) => { /* IVpnClientInfo */ });
472
- server.on('client-disconnected', ({ clientId, reason }) => { /* ... */ });
473
- server.on('started', () => { /* server listener started */ });
474
- server.on('stopped', () => { /* server listener stopped */ });
475
- ```
476
-
477
- ## 🌐 Transport Modes
478
-
479
- smartvpn supports three transport protocols. The smartvpn-native transports (WebSocket + QUIC) share the same encryption, framing, and QoS pipeline. WireGuard mode uses the standard WireGuard protocol for broad interoperability.
480
-
481
- ### WebSocket (default for smartvpn-native)
482
-
483
- - Works through Cloudflare, reverse proxies, and HTTP load balancers
484
- - Reliable delivery only (no datagram support)
485
- - URL format: `wss://host/path` or `ws://host:port/path`
486
-
487
- ### QUIC
488
-
489
- - Lower latency, built-in multiplexing, 0-RTT connection establishment
490
- - Supports **unreliable datagrams** for IP packets (with automatic fallback to reliable if oversized)
491
- - Certificate hash pinning — no CA chain needed, WireGuard-style trust
492
- - URL format: `host:port`
493
- - ALPN protocol: `smartvpn`
494
-
495
- ### WireGuard
496
-
497
- - Standard WireGuard protocol via `boringtun` (userspace, no kernel module)
498
- - Compatible with **all WireGuard clients** — iOS, Android, macOS, Windows, Linux, routers
499
- - X25519 key exchange, ChaCha20-Poly1305 encryption
500
- - Dynamic peer management at runtime (add/remove without restart)
501
- - Optional preshared keys for post-quantum defense-in-depth
502
- - Generate `.conf` files for standard clients via `WgConfigGenerator`
503
- - Default port: `51820/UDP`
504
-
505
- ### Auto-Transport (Recommended for smartvpn-native)
506
-
507
- The default `transport: 'auto'` mode gives you the best of both worlds:
508
-
509
- 1. Extract `host:port` from the WebSocket URL
510
- 2. Attempt QUIC connection (3-second timeout)
511
- 3. If QUIC fails or times out → fall back to WebSocket
512
- 4. Completely transparent to the application
513
-
514
- ```typescript
515
- await client.connect({
516
- serverUrl: 'wss://vpn.example.com/tunnel',
517
- serverPublicKey: '...',
518
- transport: 'auto', // default — QUIC first, WS fallback
519
- });
225
+ // unit.platform → 'linux' | 'macos'
226
+ // unit.content → systemd unit file or launchd plist
227
+ // unit.installPath /etc/systemd/system/smartvpn-server.service
520
228
  ```
521
229
 
522
- ### Server Dual-Mode / Multi-Mode
523
-
524
- The server can listen on multiple transports simultaneously:
230
+ ## API Reference 📖
231
+
232
+ ### Classes
233
+
234
+ | Class | Description |
235
+ |-------|-------------|
236
+ | `VpnServer` | Manages the Rust daemon in server mode. Hub methods for client CRUD. |
237
+ | `VpnClient` | Manages the Rust daemon in client mode. Connect, disconnect, telemetry. |
238
+ | `VpnBridge<T>` | Low-level typed IPC bridge (stdio or Unix socket). |
239
+ | `VpnConfig` | Static config validation and file I/O. |
240
+ | `VpnInstaller` | Generates systemd/launchd service files. |
241
+ | `WgConfigGenerator` | Generates standard WireGuard `.conf` files. |
242
+
243
+ ### Key Interfaces
244
+
245
+ | Interface | Purpose |
246
+ |-----------|---------|
247
+ | `IVpnServerConfig` | Server configuration (listen addr, keys, subnet, transport mode, clients, proxy protocol) |
248
+ | `IVpnClientConfig` | Client configuration (server URL, keys, transport, WG options) |
249
+ | `IClientEntry` | Server-side client definition (ID, keys, security, priority, tags, expiry) |
250
+ | `IClientSecurity` | Per-client ACLs and rate limits (SmartProxy-aligned naming) |
251
+ | `IClientRateLimit` | Rate limiting config (bytesPerSec, burstBytes) |
252
+ | `IClientConfigBundle` | Full config bundle returned by `createClient()` |
253
+ | `IVpnClientInfo` | Connected client info (IP, stats, authenticated key, remote addr) |
254
+ | `IVpnConnectionQuality` | RTT, jitter, loss ratio, link health |
255
+ | `IVpnKeypair` | Base64-encoded public/private key pair |
256
+
257
+ ### Server IPC Commands
258
+
259
+ | Command | Description |
260
+ |---------|-------------|
261
+ | `start` / `stop` | Start/stop the VPN listener |
262
+ | `createClient` | Generate keys, assign IP, return config bundle |
263
+ | `removeClient` / `getClient` / `listRegisteredClients` | Client registry CRUD |
264
+ | `updateClient` / `enableClient` / `disableClient` | Modify client state |
265
+ | `rotateClientKey` | Fresh keypairs + new config bundle |
266
+ | `exportClientConfig` | Re-export as SmartVPN config or WireGuard `.conf` |
267
+ | `listClients` / `disconnectClient` | Manage live connections |
268
+ | `setClientRateLimit` / `removeClientRateLimit` | Runtime rate limit adjustments |
269
+ | `getStatus` / `getStatistics` / `getClientTelemetry` | Monitoring |
270
+ | `generateKeypair` / `generateWgKeypair` / `generateClientKeypair` | Key generation |
271
+ | `addWgPeer` / `removeWgPeer` / `listWgPeers` | WireGuard peer management |
272
+
273
+ ### Client IPC Commands
274
+
275
+ | Command | Description |
276
+ |---------|-------------|
277
+ | `connect` / `disconnect` | Manage the tunnel |
278
+ | `getStatus` / `getStatistics` | Connection state and traffic stats |
279
+ | `getConnectionQuality` | RTT, jitter, loss, link health |
280
+ | `getMtuInfo` | MTU and overhead details |
281
+
282
+ ## Transport Modes 🔀
283
+
284
+ ### Server Configuration
525
285
 
526
286
  ```typescript
527
- // WebSocket + QUIC (dual mode)
528
- await server.start({
529
- listenAddr: '0.0.0.0:443', // WebSocket listener
530
- quicListenAddr: '0.0.0.0:4433', // QUIC listener (optional, defaults to listenAddr)
531
- transportMode: 'both', // 'websocket' | 'quic' | 'both' | 'wireguard'
532
- quicIdleTimeoutSecs: 30,
533
- // ... other config
534
- });
535
-
536
- // WireGuard standalone
537
- await server.start({
538
- listenAddr: '0.0.0.0:51820',
539
- transportMode: 'wireguard',
540
- wgListenPort: 51820,
541
- wgPeers: [{ publicKey: '...', allowedIps: ['10.8.0.2/32'] }],
542
- // ... other config
543
- });
544
- ```
545
-
546
- When using `'both'` mode, the server logs the QUIC certificate hash on startup — share this with clients for cert pinning.
287
+ // WebSocket only
288
+ { transportMode: 'websocket', listenAddr: '0.0.0.0:443' }
547
289
 
548
- ## 📊 QoS System
290
+ // QUIC only
291
+ { transportMode: 'quic', listenAddr: '0.0.0.0:443' }
549
292
 
550
- The Rust daemon includes a full QoS stack that operates on decrypted IP packets:
293
+ // Both (WS + QUIC on same or different ports)
294
+ { transportMode: 'both', listenAddr: '0.0.0.0:443', quicListenAddr: '0.0.0.0:4433' }
551
295
 
552
- ### Adaptive Keepalive
553
-
554
- The keepalive system automatically adjusts its interval based on connection quality:
555
-
556
- | Link Health | Keepalive Interval | Triggered When |
557
- |-------------|-------------------|----------------|
558
- | 🟢 Healthy | 60s | Jitter < 30ms, loss < 2%, no timeouts |
559
- | 🟡 Degraded | 30s | Jitter > 50ms, loss > 5%, or 1+ timeout |
560
- | 🔴 Critical | 10s | Loss > 20% or 2+ consecutive timeouts |
561
-
562
- State transitions include hysteresis (3 consecutive good checks to upgrade, 2 to recover) to prevent flapping. Dead peer detection fires after 3 consecutive timeouts in Critical state.
563
-
564
- ### Packet Classification
565
-
566
- IP packets are classified into three priority levels by inspecting headers (no deep packet inspection):
567
-
568
- | Priority | Traffic |
569
- |----------|---------|
570
- | **High** | ICMP, DNS (port 53), SSH (port 22), small packets (< 128 bytes) |
571
- | **Normal** | Everything else |
572
- | **Low** | Bulk flows exceeding 1 MB within a 60s window |
573
-
574
- Priority channels drain with biased `tokio::select!` — high-priority packets always go first.
575
-
576
- ### Smart Packet Dropping
577
-
578
- Under backpressure, packets are dropped intelligently:
579
-
580
- 1. **Low** queue full → drop silently
581
- 2. **Normal** queue full → drop
582
- 3. **High** queue full → wait 5ms, then drop as last resort
583
-
584
- Drop statistics are tracked per priority level and exposed via telemetry.
585
-
586
- ### Per-Client Rate Limiting
587
-
588
- Token bucket algorithm with byte granularity:
589
-
590
- ```typescript
591
- // Set: 10 MB/s sustained, 20 MB burst
592
- await server.setClientRateLimit('client-id', 10_000_000, 20_000_000);
593
-
594
- // Check drops via telemetry
595
- const t = await server.getClientTelemetry('client-id');
596
- console.log(`Dropped: ${t.packetsDropped} packets, ${t.bytesDropped} bytes`);
597
-
598
- // Remove limit
599
- await server.removeClientRateLimit('client-id');
296
+ // WireGuard
297
+ { transportMode: 'wireguard', wgListenPort: 51820, wgPeers: [...] }
600
298
  ```
601
299
 
602
- Rate limits can be changed live without disconnecting the client.
603
-
604
- ### Path MTU
605
-
606
- Tunnel overhead is calculated precisely:
607
-
608
- | Layer | Bytes |
609
- |-------|-------|
610
- | IP header | 20 |
611
- | TCP header (with timestamps) | 32 |
612
- | WebSocket framing | 6 |
613
- | VPN frame header | 5 |
614
- | Noise AEAD tag | 16 |
615
- | **Total overhead** | **79** |
616
-
617
- For a standard 1500-byte Ethernet link, effective TUN MTU = **1421 bytes**. The default TUN MTU of 1420 is conservative and correct. Oversized packets get an ICMP "Fragmentation Needed" (Type 3, Code 4) written back into the TUN, so the source TCP adjusts its MSS automatically.
618
-
619
- ## 🔐 Security Model
620
-
621
- ### smartvpn-native (WebSocket / QUIC)
622
-
623
- The VPN uses a **Noise NK** handshake pattern:
624
-
625
- 1. **NK** = client does **N**ot authenticate, but **K**nows the server's static public key
626
- 2. The client generates an ephemeral keypair, performs `e, es` (DH with server's static key)
627
- 3. Server responds with `e, ee` (DH with both ephemeral keys)
628
- 4. Result: forward-secret transport keys derived from both DH operations
629
-
630
- Post-handshake, all IP packets are encrypted with **XChaCha20-Poly1305**:
631
- - 24-byte random nonces (no counter synchronization needed)
632
- - 16-byte authentication tags
633
- - Wire format: `[nonce:24B][ciphertext:var][tag:16B]`
634
-
635
- ### WireGuard Mode
636
-
637
- Uses the standard [Noise IKpsk2](https://www.wireguard.com/protocol/) handshake:
638
- - **X25519** key exchange (Curve25519 Diffie-Hellman)
639
- - **ChaCha20-Poly1305** AEAD encryption
640
- - Optional **preshared keys** for post-quantum defense-in-depth
641
- - Implemented via `boringtun` — Cloudflare's userspace WireGuard in Rust
642
-
643
- ### QUIC Certificate Pinning
644
-
645
- 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:
300
+ ### Client Configuration
646
301
 
647
302
  ```typescript
648
- // Server logs the cert hash on startup:
649
- // "QUIC cert hash: <BASE64_HASH>"
650
-
651
- // Client pins it:
652
- await client.connect({
653
- serverUrl: 'vpn.example.com:443',
654
- transport: 'quic',
655
- serverCertHash: '<BASE64_HASH>',
656
- serverPublicKey: '...',
657
- });
658
- ```
303
+ // Auto (tries QUIC first, falls back to WS)
304
+ { transport: 'auto', serverUrl: 'wss://vpn.example.com' }
659
305
 
660
- ## 📦 Binary Protocol
306
+ // Explicit QUIC with certificate pinning
307
+ { transport: 'quic', serverUrl: '1.2.3.4:4433', serverCertHash: '<sha256-base64>' }
661
308
 
662
- Inside the tunnel (both WebSocket and QUIC reliable channels), packets use a simple binary framing:
663
-
664
- ```
665
- ┌──────────┬──────────┬────────────────────┐
666
- │ Type (1B)│ Len (4B) │ Payload (variable) │
667
- └──────────┴──────────┴────────────────────┘
309
+ // WireGuard
310
+ { transport: 'wireguard', wgPrivateKey: '...', wgEndpoint: 'vpn.example.com:51820', ... }
668
311
  ```
669
312
 
670
- | Type | Value | Description |
671
- |------|-------|-------------|
672
- | `HandshakeInit` | `0x01` | Client → Server handshake |
673
- | `HandshakeResp` | `0x02` | Server → Client handshake |
674
- | `IpPacket` | `0x10` | Encrypted IP packet |
675
- | `Keepalive` | `0x20` | App-level ping (8-byte timestamp payload) |
676
- | `KeepaliveAck` | `0x21` | App-level pong (echoes timestamp for RTT) |
677
- | `SessionResume` | `0x30` | Resume a dropped session |
678
- | `SessionResumeOk` | `0x31` | Resume accepted |
679
- | `SessionResumeErr` | `0x32` | Resume rejected |
680
- | `Disconnect` | `0x3F` | Graceful disconnect |
313
+ ## Cryptography 🔑
681
314
 
682
- 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.
315
+ | Layer | Algorithm | Purpose |
316
+ |-------|-----------|---------|
317
+ | **Handshake** | Noise IK (X25519 + ChaChaPoly + BLAKE2s) | Mutual authentication + key exchange |
318
+ | **Transport** | Noise transport state (ChaChaPoly) | All post-handshake data encryption |
319
+ | **Additional** | XChaCha20-Poly1305 | Extended nonce space for data-at-rest |
320
+ | **WireGuard** | X25519 + ChaCha20-Poly1305 (via boringtun) | Standard WireGuard crypto |
683
321
 
684
- > **Note:** WireGuard mode uses the standard WireGuard wire protocol, not this binary framing.
322
+ ## Binary Protocol 📡
685
323
 
686
- ## 🛠️ Rust Daemon CLI
324
+ All frames use `[type:1B][length:4B][payload:NB]` with a 64KB max payload:
687
325
 
688
- ```bash
689
- # Development: stdio management (JSON lines on stdin/stdout)
690
- smartvpn_daemon --management --mode client
691
- smartvpn_daemon --management --mode server
326
+ | Type | Hex | Direction | Description |
327
+ |------|-----|-----------|-------------|
328
+ | HandshakeInit | `0x01` | Client → Server | Noise IK first message |
329
+ | HandshakeResp | `0x02` | Server → Client | Noise IK response |
330
+ | IpPacket | `0x10` | Bidirectional | Encrypted tunnel data |
331
+ | Keepalive | `0x20` | Client → Server | App-level keepalive (not WS ping) |
332
+ | KeepaliveAck | `0x21` | Server → Client | Keepalive response with RTT payload |
333
+ | Disconnect | `0x3F` | Bidirectional | Graceful disconnect |
692
334
 
693
- # Production: Unix socket management
694
- smartvpn_daemon --management-socket /var/run/smartvpn.sock --mode server
695
-
696
- # Generate a Noise keypair
697
- smartvpn_daemon --generate-keypair
698
- ```
699
-
700
- ## 🔧 Building from Source
335
+ ## Development 🛠️
701
336
 
702
337
  ```bash
703
338
  # Install dependencies
704
339
  pnpm install
705
340
 
706
- # Build TypeScript + cross-compile Rust (amd64 + arm64)
341
+ # Build (TypeScript + Rust cross-compile)
707
342
  pnpm build
708
343
 
709
- # Build Rust only (debug)
710
- cd rust && cargo build
711
-
712
- # Run all tests (93 Rust + 77 TypeScript)
713
- cd rust && cargo test
344
+ # Run all tests (79 TS + 129 Rust = 208 tests)
714
345
  pnpm test
715
- ```
716
-
717
- ## 📘 TypeScript Interfaces
718
-
719
- <details>
720
- <summary>Click to expand full type definitions</summary>
721
-
722
- ```typescript
723
- // Transport options
724
- type TVpnTransportOptions =
725
- | { transport: 'stdio' }
726
- | {
727
- transport: 'socket';
728
- socketPath: string;
729
- autoReconnect?: boolean;
730
- reconnectBaseDelayMs?: number;
731
- reconnectMaxDelayMs?: number;
732
- maxReconnectAttempts?: number;
733
- };
734
-
735
- // Client config
736
- interface IVpnClientConfig {
737
- serverUrl: string; // WS: 'wss://host/path' | QUIC: 'host:port'
738
- serverPublicKey: string; // Base64-encoded Noise static key (or WG public key)
739
- transport?: 'auto' | 'websocket' | 'quic' | 'wireguard'; // Default: 'auto'
740
- serverCertHash?: string; // SHA-256 cert hash (base64) for QUIC pinning
741
- dns?: string[];
742
- mtu?: number;
743
- keepaliveIntervalSecs?: number;
744
- // WireGuard-specific
745
- wgPrivateKey?: string; // Client private key (base64, X25519)
746
- wgAddress?: string; // Client TUN address (e.g. 10.8.0.2)
747
- wgAddressPrefix?: number; // Address prefix length (default: 24)
748
- wgPresharedKey?: string; // Optional preshared key (base64)
749
- wgPersistentKeepalive?: number; // Persistent keepalive interval (seconds)
750
- wgEndpoint?: string; // Server endpoint (host:port)
751
- wgAllowedIps?: string[]; // Allowed IPs (CIDR strings)
752
- }
753
346
 
754
- // Server config
755
- interface IVpnServerConfig {
756
- listenAddr: string;
757
- privateKey: string;
758
- publicKey: string;
759
- subnet: string;
760
- tlsCert?: string;
761
- tlsKey?: string;
762
- dns?: string[];
763
- mtu?: number;
764
- keepaliveIntervalSecs?: number;
765
- enableNat?: boolean;
766
- transportMode?: 'websocket' | 'quic' | 'both' | 'wireguard';
767
- quicListenAddr?: string;
768
- quicIdleTimeoutSecs?: number;
769
- defaultRateLimitBytesPerSec?: number;
770
- defaultBurstBytes?: number;
771
- // WireGuard-specific
772
- wgListenPort?: number; // UDP port (default: 51820)
773
- wgPeers?: IWgPeerConfig[]; // Initial peers
774
- }
775
-
776
- // WireGuard peer config
777
- interface IWgPeerConfig {
778
- publicKey: string; // Peer's X25519 public key (base64)
779
- presharedKey?: string; // Optional preshared key (base64)
780
- allowedIps: string[]; // Allowed IP ranges (CIDR)
781
- endpoint?: string; // Peer endpoint (host:port)
782
- persistentKeepalive?: number; // Keepalive interval (seconds)
783
- }
784
-
785
- // WireGuard peer info (with live stats)
786
- interface IWgPeerInfo {
787
- publicKey: string;
788
- allowedIps: string[];
789
- endpoint?: string;
790
- persistentKeepalive?: number;
791
- bytesSent: number;
792
- bytesReceived: number;
793
- packetsSent: number;
794
- packetsReceived: number;
795
- lastHandshakeTime?: string;
796
- }
797
-
798
- // Status
799
- type TVpnConnectionState = 'disconnected' | 'connecting' | 'handshaking'
800
- | 'connected' | 'reconnecting' | 'error';
801
-
802
- interface IVpnStatus {
803
- state: TVpnConnectionState;
804
- assignedIp?: string;
805
- serverAddr?: string;
806
- connectedSince?: string;
807
- lastError?: string;
808
- }
809
-
810
- // Statistics
811
- interface IVpnStatistics {
812
- bytesSent: number;
813
- bytesReceived: number;
814
- packetsSent: number;
815
- packetsReceived: number;
816
- keepalivesSent: number;
817
- keepalivesReceived: number;
818
- uptimeSeconds: number;
819
- quality?: IVpnConnectionQuality;
820
- }
821
-
822
- interface IVpnServerStatistics extends IVpnStatistics {
823
- activeClients: number;
824
- totalConnections: number;
825
- }
826
-
827
- // Connection quality (QoS)
828
- type TVpnLinkHealth = 'healthy' | 'degraded' | 'critical';
829
-
830
- interface IVpnConnectionQuality {
831
- srttMs: number;
832
- jitterMs: number;
833
- minRttMs: number;
834
- maxRttMs: number;
835
- lossRatio: number;
836
- consecutiveTimeouts: number;
837
- linkHealth: TVpnLinkHealth;
838
- currentKeepaliveIntervalSecs: number;
839
- }
840
-
841
- // MTU info
842
- interface IVpnMtuInfo {
843
- tunMtu: number;
844
- effectiveMtu: number;
845
- linkMtu: number;
846
- overheadBytes: number;
847
- oversizedPacketsDropped: number;
848
- icmpTooBigSent: number;
849
- }
347
+ # Run Rust tests directly
348
+ cd rust && cargo test
850
349
 
851
- // Client info (with QoS fields)
852
- interface IVpnClientInfo {
853
- clientId: string;
854
- assignedIp: string;
855
- connectedSince: string;
856
- bytesSent: number;
857
- bytesReceived: number;
858
- packetsDropped: number;
859
- bytesDropped: number;
860
- lastKeepaliveAt?: string;
861
- keepalivesReceived: number;
862
- rateLimitBytesPerSec?: number;
863
- burstBytes?: number;
864
- }
350
+ # Run a specific TS test
351
+ tstest test/test.flowcontrol.node.ts --verbose
352
+ ```
865
353
 
866
- // Per-client telemetry
867
- interface IVpnClientTelemetry {
868
- clientId: string;
869
- assignedIp: string;
870
- lastKeepaliveAt?: string;
871
- keepalivesReceived: number;
872
- packetsDropped: number;
873
- bytesDropped: number;
874
- bytesReceived: number;
875
- bytesSent: number;
876
- rateLimitBytesPerSec?: number;
877
- burstBytes?: number;
878
- }
354
+ ### Project Structure
879
355
 
880
- interface IVpnKeypair {
881
- publicKey: string;
882
- privateKey: string;
883
- }
884
356
  ```
885
-
886
- </details>
357
+ smartvpn/
358
+ ├── ts/ # TypeScript control plane
359
+ │ ├── index.ts # All exports
360
+ │ ├── smartvpn.interfaces.ts # Interfaces, types, IPC command maps
361
+ │ ├── smartvpn.classes.vpnserver.ts
362
+ │ ├── smartvpn.classes.vpnclient.ts
363
+ │ ├── smartvpn.classes.vpnbridge.ts
364
+ │ ├── smartvpn.classes.vpnconfig.ts
365
+ │ ├── smartvpn.classes.vpninstaller.ts
366
+ │ └── smartvpn.classes.wgconfig.ts
367
+ ├── rust/ # Rust data plane daemon
368
+ │ └── src/
369
+ │ ├── main.rs # CLI entry point
370
+ │ ├── server.rs # VPN server + hub methods
371
+ │ ├── client.rs # VPN client
372
+ │ ├── crypto.rs # Noise IK + XChaCha20
373
+ │ ├── client_registry.rs # Client database
374
+ │ ├── acl.rs # ACL engine
375
+ │ ├── proxy_protocol.rs # PROXY protocol v2 parser
376
+ │ ├── management.rs # JSON-lines IPC
377
+ │ ├── transport.rs # WebSocket transport
378
+ │ ├── quic_transport.rs # QUIC transport
379
+ │ ├── wireguard.rs # WireGuard (boringtun)
380
+ │ ├── codec.rs # Binary frame protocol
381
+ │ ├── keepalive.rs # Adaptive keepalives
382
+ │ ├── ratelimit.rs # Token bucket
383
+ │ └── ... # tunnel, network, telemetry, qos, mtu, reconnect
384
+ ├── test/ # 9 test files (79 tests)
385
+ ├── dist_ts/ # Compiled TypeScript
386
+ └── dist_rust/ # Cross-compiled binaries (linux amd64 + arm64)
387
+ ```
887
388
 
888
389
  ## License and Legal Information
889
390
 
890
- This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./license.md) file.
391
+ This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [license](./license.md) file.
891
392
 
892
393
  **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.
893
394
 
@@ -899,7 +400,7 @@ Use of these trademarks must comply with Task Venture Capital GmbH's Trademark G
899
400
 
900
401
  ### Company Information
901
402
 
902
- Task Venture Capital GmbH
403
+ Task Venture Capital GmbH
903
404
  Registered at District Court Bremen HRB 35230 HB, Germany
904
405
 
905
406
  For any legal inquiries or further information, please contact us via email at hello@task.vc.