@push.rocks/smartvpn 1.1.0 → 1.3.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.
|
Binary file
|
|
Binary file
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartvpn',
|
|
6
|
-
version: '1.
|
|
6
|
+
version: '1.3.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=
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @push.rocks/smartvpn
|
|
2
2
|
|
|
3
|
-
A high-performance VPN
|
|
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
5
|
## Issue Reporting and Security
|
|
6
6
|
|
|
@@ -9,8 +9,6 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
npm install @push.rocks/smartvpn
|
|
13
|
-
# or
|
|
14
12
|
pnpm install @push.rocks/smartvpn
|
|
15
13
|
```
|
|
16
14
|
|
|
@@ -18,17 +16,21 @@ pnpm install @push.rocks/smartvpn
|
|
|
18
16
|
|
|
19
17
|
```
|
|
20
18
|
TypeScript (control plane) Rust (data plane)
|
|
21
|
-
┌──────────────────────────┐
|
|
22
|
-
│ VpnClient / VpnServer │ │ smartvpn_daemon
|
|
23
|
-
│ └─ VpnBridge │──stdio/──▶ │ ├─ management (JSON IPC)
|
|
24
|
-
│ └─ RustBridge │ socket │ ├─ transport (WebSocket/TLS)
|
|
25
|
-
│ (smartrust) │ │ ├─ crypto (Noise NK +
|
|
26
|
-
└──────────────────────────┘ │ ├─ codec (binary framing)
|
|
27
|
-
│ ├─ keepalive (
|
|
28
|
-
│ ├─
|
|
29
|
-
│ ├─
|
|
30
|
-
│
|
|
31
|
-
|
|
19
|
+
┌──────────────────────────┐ ┌────────────────────────────────────┐
|
|
20
|
+
│ VpnClient / VpnServer │ │ smartvpn_daemon │
|
|
21
|
+
│ └─ VpnBridge │──stdio/──▶ │ ├─ management (JSON IPC) │
|
|
22
|
+
│ └─ RustBridge │ socket │ ├─ transport (WebSocket/TLS) │
|
|
23
|
+
│ (smartrust) │ │ ├─ crypto (Noise NK + XCha20) │
|
|
24
|
+
└──────────────────────────┘ │ ├─ codec (binary framing) │
|
|
25
|
+
│ ├─ keepalive (adaptive state FSM) │
|
|
26
|
+
│ ├─ telemetry (RTT/jitter/loss) │
|
|
27
|
+
│ ├─ qos (classify + priority Q) │
|
|
28
|
+
│ ├─ ratelimit (token bucket) │
|
|
29
|
+
│ ├─ mtu (overhead calc + ICMP) │
|
|
30
|
+
│ ├─ tunnel (TUN device) │
|
|
31
|
+
│ ├─ network (NAT/IP pool) │
|
|
32
|
+
│ └─ reconnect (exp. backoff) │
|
|
33
|
+
└────────────────────────────────────┘
|
|
32
34
|
```
|
|
33
35
|
|
|
34
36
|
**Key design decisions:**
|
|
@@ -37,8 +39,10 @@ TypeScript (control plane) Rust (data plane)
|
|
|
37
39
|
|----------|--------|-----|
|
|
38
40
|
| Transport | WebSocket over HTTPS | Works through Cloudflare and other terminating proxies |
|
|
39
41
|
| Encryption | Noise NK + XChaCha20-Poly1305 | Strong forward secrecy, large nonce space (no counter needed) |
|
|
40
|
-
| Keepalive |
|
|
41
|
-
|
|
|
42
|
+
| Keepalive | Adaptive app-level pings | Cloudflare drops WS pings; interval adapts to link health (10–60s) |
|
|
43
|
+
| QoS | Packet classification + priority queues | DNS/SSH/ICMP always drain first; bulk flows get deprioritized |
|
|
44
|
+
| Rate limiting | Per-client token bucket | Byte-granular, dynamically reconfigurable via IPC |
|
|
45
|
+
| IPC | JSON lines over stdio / Unix socket | `stdio` for dev, `socket` for production (daemon stays alive) |
|
|
42
46
|
| Binary protocol | `[type:1B][length:4B][payload:NB]` | Minimal overhead, easy to parse at wire speed |
|
|
43
47
|
|
|
44
48
|
## 🚀 Quick Start
|
|
@@ -48,15 +52,12 @@ TypeScript (control plane) Rust (data plane)
|
|
|
48
52
|
```typescript
|
|
49
53
|
import { VpnClient } from '@push.rocks/smartvpn';
|
|
50
54
|
|
|
51
|
-
// Development: spawn the Rust daemon as a child process
|
|
52
55
|
const client = new VpnClient({
|
|
53
56
|
transport: { transport: 'stdio' },
|
|
54
57
|
});
|
|
55
58
|
|
|
56
|
-
// Start the daemon bridge
|
|
57
59
|
await client.start();
|
|
58
60
|
|
|
59
|
-
// Connect to a VPN server
|
|
60
61
|
const { assignedIp } = await client.connect({
|
|
61
62
|
serverUrl: 'wss://vpn.example.com/tunnel',
|
|
62
63
|
serverPublicKey: 'BASE64_SERVER_PUBLIC_KEY',
|
|
@@ -67,15 +68,23 @@ const { assignedIp } = await client.connect({
|
|
|
67
68
|
|
|
68
69
|
console.log(`Connected! Assigned IP: ${assignedIp}`);
|
|
69
70
|
|
|
70
|
-
//
|
|
71
|
-
const
|
|
72
|
-
console.log(
|
|
73
|
-
|
|
74
|
-
//
|
|
71
|
+
// Connection quality (adaptive keepalive + telemetry)
|
|
72
|
+
const quality = await client.getConnectionQuality();
|
|
73
|
+
console.log(quality);
|
|
74
|
+
// {
|
|
75
|
+
// srttMs: 42.5, jitterMs: 3.2, minRttMs: 38.0, maxRttMs: 67.0,
|
|
76
|
+
// lossRatio: 0.0, consecutiveTimeouts: 0,
|
|
77
|
+
// linkHealth: 'healthy', currentKeepaliveIntervalSecs: 60
|
|
78
|
+
// }
|
|
79
|
+
|
|
80
|
+
// MTU info
|
|
81
|
+
const mtu = await client.getMtuInfo();
|
|
82
|
+
console.log(mtu);
|
|
83
|
+
// { tunMtu: 1420, effectiveMtu: 1421, linkMtu: 1500, overheadBytes: 79, ... }
|
|
84
|
+
|
|
85
|
+
// Traffic stats (includes quality snapshot)
|
|
75
86
|
const stats = await client.getStatistics();
|
|
76
|
-
console.log(stats); // { bytesSent, bytesReceived, packetsSent, ... }
|
|
77
87
|
|
|
78
|
-
// Disconnect
|
|
79
88
|
await client.disconnect();
|
|
80
89
|
client.stop();
|
|
81
90
|
```
|
|
@@ -89,33 +98,44 @@ const server = new VpnServer({
|
|
|
89
98
|
transport: { transport: 'stdio' },
|
|
90
99
|
});
|
|
91
100
|
|
|
92
|
-
//
|
|
101
|
+
// Generate a Noise keypair first
|
|
102
|
+
await server.start();
|
|
103
|
+
// If you don't have keys yet:
|
|
104
|
+
const keypair = await server.generateKeypair();
|
|
105
|
+
|
|
106
|
+
// Start the VPN listener (or pass config to start() directly)
|
|
93
107
|
await server.start({
|
|
94
108
|
listenAddr: '0.0.0.0:443',
|
|
95
|
-
privateKey:
|
|
96
|
-
publicKey:
|
|
109
|
+
privateKey: keypair.privateKey,
|
|
110
|
+
publicKey: keypair.publicKey,
|
|
97
111
|
subnet: '10.8.0.0/24',
|
|
98
112
|
dns: ['1.1.1.1'],
|
|
99
113
|
mtu: 1420,
|
|
100
114
|
enableNat: true,
|
|
115
|
+
// Optional: default rate limit for all new clients
|
|
116
|
+
defaultRateLimitBytesPerSec: 10_000_000, // 10 MB/s
|
|
117
|
+
defaultBurstBytes: 20_000_000, // 20 MB burst
|
|
101
118
|
});
|
|
102
119
|
|
|
103
|
-
// Generate a Noise keypair
|
|
104
|
-
const keypair = await server.generateKeypair();
|
|
105
|
-
console.log(keypair); // { publicKey: '...', privateKey: '...' }
|
|
106
|
-
|
|
107
120
|
// List connected clients
|
|
108
121
|
const clients = await server.listClients();
|
|
109
|
-
// [{ clientId, assignedIp, connectedSince, bytesSent, bytesReceived }]
|
|
110
122
|
|
|
111
|
-
//
|
|
112
|
-
await server.
|
|
123
|
+
// Per-client rate limiting (live, no reconnect needed)
|
|
124
|
+
await server.setClientRateLimit('client-id', 5_000_000, 10_000_000);
|
|
125
|
+
await server.removeClientRateLimit('client-id'); // unlimited
|
|
126
|
+
|
|
127
|
+
// Per-client telemetry
|
|
128
|
+
const telemetry = await server.getClientTelemetry('client-id');
|
|
129
|
+
console.log(telemetry);
|
|
130
|
+
// {
|
|
131
|
+
// clientId, assignedIp, lastKeepaliveAt, keepalivesReceived,
|
|
132
|
+
// packetsDropped, bytesDropped, bytesReceived, bytesSent,
|
|
133
|
+
// rateLimitBytesPerSec, burstBytes
|
|
134
|
+
// }
|
|
113
135
|
|
|
114
|
-
//
|
|
115
|
-
|
|
116
|
-
// { bytesSent, bytesReceived, activeClients, totalConnections, ... }
|
|
136
|
+
// Kick a client
|
|
137
|
+
await server.disconnectClient('client-id');
|
|
117
138
|
|
|
118
|
-
// Stop
|
|
119
139
|
await server.stopServer();
|
|
120
140
|
server.stop();
|
|
121
141
|
```
|
|
@@ -151,7 +171,9 @@ When using socket transport, `client.stop()` closes the socket but **does not ki
|
|
|
151
171
|
| `connect(config?)` | `Promise<{ assignedIp }>` | Connect to VPN server |
|
|
152
172
|
| `disconnect()` | `Promise<void>` | Disconnect from VPN |
|
|
153
173
|
| `getStatus()` | `Promise<IVpnStatus>` | Current connection state |
|
|
154
|
-
| `getStatistics()` | `Promise<IVpnStatistics>` | Traffic
|
|
174
|
+
| `getStatistics()` | `Promise<IVpnStatistics>` | Traffic stats + connection quality |
|
|
175
|
+
| `getConnectionQuality()` | `Promise<IVpnConnectionQuality>` | RTT, jitter, loss, link health |
|
|
176
|
+
| `getMtuInfo()` | `Promise<IVpnMtuInfo>` | MTU info and overhead breakdown |
|
|
155
177
|
| `stop()` | `void` | Kill/close the daemon bridge |
|
|
156
178
|
| `running` | `boolean` | Whether bridge is active |
|
|
157
179
|
|
|
@@ -163,9 +185,12 @@ When using socket transport, `client.stop()` closes the socket but **does not ki
|
|
|
163
185
|
| `stopServer()` | `Promise<void>` | Stop the VPN server |
|
|
164
186
|
| `getStatus()` | `Promise<IVpnStatus>` | Server connection state |
|
|
165
187
|
| `getStatistics()` | `Promise<IVpnServerStatistics>` | Server stats (includes client counts) |
|
|
166
|
-
| `listClients()` | `Promise<IVpnClientInfo[]>` | Connected clients |
|
|
188
|
+
| `listClients()` | `Promise<IVpnClientInfo[]>` | Connected clients with QoS stats |
|
|
167
189
|
| `disconnectClient(id)` | `Promise<void>` | Kick a client |
|
|
168
190
|
| `generateKeypair()` | `Promise<IVpnKeypair>` | Generate Noise NK keypair |
|
|
191
|
+
| `setClientRateLimit(id, rate, burst)` | `Promise<void>` | Set per-client rate limit (bytes/sec) |
|
|
192
|
+
| `removeClientRateLimit(id)` | `Promise<void>` | Remove rate limit (unlimited) |
|
|
193
|
+
| `getClientTelemetry(id)` | `Promise<IVpnClientTelemetry>` | Per-client telemetry + drop stats |
|
|
169
194
|
| `stop()` | `void` | Kill/close the daemon bridge |
|
|
170
195
|
|
|
171
196
|
### `VpnConfig`
|
|
@@ -191,26 +216,23 @@ Generate system service units for the daemon:
|
|
|
191
216
|
```typescript
|
|
192
217
|
import { VpnInstaller } from '@push.rocks/smartvpn';
|
|
193
218
|
|
|
194
|
-
// Auto-detect platform
|
|
195
219
|
const platform = VpnInstaller.detectPlatform(); // 'linux' | 'macos' | 'windows' | 'unknown'
|
|
196
220
|
|
|
197
|
-
//
|
|
221
|
+
// Linux (systemd)
|
|
198
222
|
const unit = VpnInstaller.generateSystemdUnit({
|
|
199
223
|
binaryPath: '/usr/local/bin/smartvpn_daemon',
|
|
200
224
|
socketPath: '/var/run/smartvpn.sock',
|
|
201
225
|
mode: 'server',
|
|
202
226
|
});
|
|
203
|
-
// unit.content = full systemd .service file
|
|
204
|
-
// unit.installPath = '/etc/systemd/system/smartvpn-server.service'
|
|
205
227
|
|
|
206
|
-
//
|
|
228
|
+
// macOS (launchd)
|
|
207
229
|
const plist = VpnInstaller.generateLaunchdPlist({
|
|
208
230
|
binaryPath: '/usr/local/bin/smartvpn_daemon',
|
|
209
231
|
socketPath: '/var/run/smartvpn.sock',
|
|
210
232
|
mode: 'client',
|
|
211
233
|
});
|
|
212
234
|
|
|
213
|
-
// Auto-detect
|
|
235
|
+
// Auto-detect platform
|
|
214
236
|
const serviceUnit = VpnInstaller.generateServiceUnit({
|
|
215
237
|
binaryPath: '/usr/local/bin/smartvpn_daemon',
|
|
216
238
|
socketPath: '/var/run/smartvpn.sock',
|
|
@@ -223,8 +245,6 @@ const serviceUnit = VpnInstaller.generateServiceUnit({
|
|
|
223
245
|
Both `VpnClient` and `VpnServer` extend `EventEmitter`:
|
|
224
246
|
|
|
225
247
|
```typescript
|
|
226
|
-
client.on('status', (status) => { /* IVpnStatus */ });
|
|
227
|
-
client.on('error', (err) => { /* { message, code? } */ });
|
|
228
248
|
client.on('exit', ({ code, signal }) => { /* daemon exited */ });
|
|
229
249
|
client.on('reconnected', () => { /* socket reconnected */ });
|
|
230
250
|
|
|
@@ -232,13 +252,84 @@ server.on('client-connected', (info) => { /* IVpnClientInfo */ });
|
|
|
232
252
|
server.on('client-disconnected', ({ clientId, reason }) => { /* ... */ });
|
|
233
253
|
```
|
|
234
254
|
|
|
255
|
+
## 📊 QoS System
|
|
256
|
+
|
|
257
|
+
The Rust daemon includes a full QoS stack that operates on decrypted IP packets:
|
|
258
|
+
|
|
259
|
+
### Adaptive Keepalive
|
|
260
|
+
|
|
261
|
+
The keepalive system automatically adjusts its interval based on connection quality:
|
|
262
|
+
|
|
263
|
+
| Link Health | Keepalive Interval | Triggered When |
|
|
264
|
+
|-------------|-------------------|----------------|
|
|
265
|
+
| 🟢 Healthy | 60s | Jitter < 30ms, loss < 2%, no timeouts |
|
|
266
|
+
| 🟡 Degraded | 30s | Jitter > 50ms, loss > 5%, or 1+ timeout |
|
|
267
|
+
| 🔴 Critical | 10s | Loss > 20% or 2+ consecutive timeouts |
|
|
268
|
+
|
|
269
|
+
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.
|
|
270
|
+
|
|
271
|
+
### Packet Classification
|
|
272
|
+
|
|
273
|
+
IP packets are classified into three priority levels by inspecting headers (no deep packet inspection):
|
|
274
|
+
|
|
275
|
+
| Priority | Traffic |
|
|
276
|
+
|----------|---------|
|
|
277
|
+
| **High** | ICMP, DNS (port 53), SSH (port 22), small packets (< 128 bytes) |
|
|
278
|
+
| **Normal** | Everything else |
|
|
279
|
+
| **Low** | Bulk flows exceeding 1 MB within a 60s window |
|
|
280
|
+
|
|
281
|
+
Priority channels drain with biased `tokio::select!` — high-priority packets always go first.
|
|
282
|
+
|
|
283
|
+
### Smart Packet Dropping
|
|
284
|
+
|
|
285
|
+
Under backpressure, packets are dropped intelligently:
|
|
286
|
+
|
|
287
|
+
1. **Low** queue full → drop silently
|
|
288
|
+
2. **Normal** queue full → drop
|
|
289
|
+
3. **High** queue full → wait 5ms, then drop as last resort
|
|
290
|
+
|
|
291
|
+
Drop statistics are tracked per priority level and exposed via telemetry.
|
|
292
|
+
|
|
293
|
+
### Per-Client Rate Limiting
|
|
294
|
+
|
|
295
|
+
Token bucket algorithm with byte granularity:
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
// Set: 10 MB/s sustained, 20 MB burst
|
|
299
|
+
await server.setClientRateLimit('client-id', 10_000_000, 20_000_000);
|
|
300
|
+
|
|
301
|
+
// Check drops via telemetry
|
|
302
|
+
const t = await server.getClientTelemetry('client-id');
|
|
303
|
+
console.log(`Dropped: ${t.packetsDropped} packets, ${t.bytesDropped} bytes`);
|
|
304
|
+
|
|
305
|
+
// Remove limit
|
|
306
|
+
await server.removeClientRateLimit('client-id');
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Rate limits can be changed live without disconnecting the client.
|
|
310
|
+
|
|
311
|
+
### Path MTU
|
|
312
|
+
|
|
313
|
+
Tunnel overhead is calculated precisely:
|
|
314
|
+
|
|
315
|
+
| Layer | Bytes |
|
|
316
|
+
|-------|-------|
|
|
317
|
+
| IP header | 20 |
|
|
318
|
+
| TCP header (with timestamps) | 32 |
|
|
319
|
+
| WebSocket framing | 6 |
|
|
320
|
+
| VPN frame header | 5 |
|
|
321
|
+
| Noise AEAD tag | 16 |
|
|
322
|
+
| **Total overhead** | **79** |
|
|
323
|
+
|
|
324
|
+
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.
|
|
325
|
+
|
|
235
326
|
## 🔐 Security Model
|
|
236
327
|
|
|
237
328
|
The VPN uses a **Noise NK** handshake pattern:
|
|
238
329
|
|
|
239
330
|
1. **NK** = client does **N**ot authenticate, but **K**nows the server's static public key
|
|
240
|
-
2. The client generates an ephemeral keypair, performs `e, es` (
|
|
241
|
-
3. Server responds with `e, ee` (
|
|
331
|
+
2. The client generates an ephemeral keypair, performs `e, es` (DH with server's static key)
|
|
332
|
+
3. Server responds with `e, ee` (DH with both ephemeral keys)
|
|
242
333
|
4. Result: forward-secret transport keys derived from both DH operations
|
|
243
334
|
|
|
244
335
|
Post-handshake, all IP packets are encrypted with **XChaCha20-Poly1305**:
|
|
@@ -261,8 +352,8 @@ Inside the WebSocket tunnel, packets use a simple binary framing:
|
|
|
261
352
|
| `HandshakeInit` | `0x01` | Client → Server handshake |
|
|
262
353
|
| `HandshakeResp` | `0x02` | Server → Client handshake |
|
|
263
354
|
| `IpPacket` | `0x10` | Encrypted IP packet |
|
|
264
|
-
| `Keepalive` | `0x20` | App-level ping |
|
|
265
|
-
| `KeepaliveAck` | `0x21` | App-level pong |
|
|
355
|
+
| `Keepalive` | `0x20` | App-level ping (8-byte timestamp payload) |
|
|
356
|
+
| `KeepaliveAck` | `0x21` | App-level pong (echoes timestamp for RTT) |
|
|
266
357
|
| `SessionResume` | `0x30` | Resume a dropped session |
|
|
267
358
|
| `SessionResumeOk` | `0x31` | Resume accepted |
|
|
268
359
|
| `SessionResumeErr` | `0x32` | Resume rejected |
|
|
@@ -270,8 +361,6 @@ Inside the WebSocket tunnel, packets use a simple binary framing:
|
|
|
270
361
|
|
|
271
362
|
## 🛠️ Rust Daemon CLI
|
|
272
363
|
|
|
273
|
-
The Rust binary supports several modes:
|
|
274
|
-
|
|
275
364
|
```bash
|
|
276
365
|
# Development: stdio management (JSON lines on stdin/stdout)
|
|
277
366
|
smartvpn_daemon --management --mode client
|
|
@@ -290,16 +379,14 @@ smartvpn_daemon --generate-keypair
|
|
|
290
379
|
# Install dependencies
|
|
291
380
|
pnpm install
|
|
292
381
|
|
|
293
|
-
# Build TypeScript + cross-compile Rust
|
|
382
|
+
# Build TypeScript + cross-compile Rust (amd64 + arm64)
|
|
294
383
|
pnpm build
|
|
295
384
|
|
|
296
385
|
# Build Rust only (debug)
|
|
297
386
|
cd rust && cargo build
|
|
298
387
|
|
|
299
|
-
# Run Rust
|
|
388
|
+
# Run all tests (71 Rust + 32 TypeScript)
|
|
300
389
|
cd rust && cargo test
|
|
301
|
-
|
|
302
|
-
# Run TypeScript tests
|
|
303
390
|
pnpm test
|
|
304
391
|
```
|
|
305
392
|
|
|
@@ -323,25 +410,27 @@ type TVpnTransportOptions =
|
|
|
323
410
|
|
|
324
411
|
// Client config
|
|
325
412
|
interface IVpnClientConfig {
|
|
326
|
-
serverUrl: string;
|
|
327
|
-
serverPublicKey: string;
|
|
413
|
+
serverUrl: string;
|
|
414
|
+
serverPublicKey: string;
|
|
328
415
|
dns?: string[];
|
|
329
|
-
mtu?: number;
|
|
330
|
-
keepaliveIntervalSecs?: number;
|
|
416
|
+
mtu?: number;
|
|
417
|
+
keepaliveIntervalSecs?: number;
|
|
331
418
|
}
|
|
332
419
|
|
|
333
420
|
// Server config
|
|
334
421
|
interface IVpnServerConfig {
|
|
335
|
-
listenAddr: string;
|
|
336
|
-
privateKey: string;
|
|
337
|
-
publicKey: string;
|
|
338
|
-
subnet: string;
|
|
422
|
+
listenAddr: string;
|
|
423
|
+
privateKey: string;
|
|
424
|
+
publicKey: string;
|
|
425
|
+
subnet: string;
|
|
339
426
|
tlsCert?: string;
|
|
340
427
|
tlsKey?: string;
|
|
341
428
|
dns?: string[];
|
|
342
429
|
mtu?: number;
|
|
343
430
|
keepaliveIntervalSecs?: number;
|
|
344
431
|
enableNat?: boolean;
|
|
432
|
+
defaultRateLimitBytesPerSec?: number;
|
|
433
|
+
defaultBurstBytes?: number;
|
|
345
434
|
}
|
|
346
435
|
|
|
347
436
|
// Status
|
|
@@ -365,6 +454,7 @@ interface IVpnStatistics {
|
|
|
365
454
|
keepalivesSent: number;
|
|
366
455
|
keepalivesReceived: number;
|
|
367
456
|
uptimeSeconds: number;
|
|
457
|
+
quality?: IVpnConnectionQuality;
|
|
368
458
|
}
|
|
369
459
|
|
|
370
460
|
interface IVpnServerStatistics extends IVpnStatistics {
|
|
@@ -372,12 +462,57 @@ interface IVpnServerStatistics extends IVpnStatistics {
|
|
|
372
462
|
totalConnections: number;
|
|
373
463
|
}
|
|
374
464
|
|
|
465
|
+
// Connection quality (QoS)
|
|
466
|
+
type TVpnLinkHealth = 'healthy' | 'degraded' | 'critical';
|
|
467
|
+
|
|
468
|
+
interface IVpnConnectionQuality {
|
|
469
|
+
srttMs: number;
|
|
470
|
+
jitterMs: number;
|
|
471
|
+
minRttMs: number;
|
|
472
|
+
maxRttMs: number;
|
|
473
|
+
lossRatio: number;
|
|
474
|
+
consecutiveTimeouts: number;
|
|
475
|
+
linkHealth: TVpnLinkHealth;
|
|
476
|
+
currentKeepaliveIntervalSecs: number;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// MTU info
|
|
480
|
+
interface IVpnMtuInfo {
|
|
481
|
+
tunMtu: number;
|
|
482
|
+
effectiveMtu: number;
|
|
483
|
+
linkMtu: number;
|
|
484
|
+
overheadBytes: number;
|
|
485
|
+
oversizedPacketsDropped: number;
|
|
486
|
+
icmpTooBigSent: number;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// Client info (with QoS fields)
|
|
375
490
|
interface IVpnClientInfo {
|
|
376
491
|
clientId: string;
|
|
377
492
|
assignedIp: string;
|
|
378
493
|
connectedSince: string;
|
|
379
494
|
bytesSent: number;
|
|
380
495
|
bytesReceived: number;
|
|
496
|
+
packetsDropped: number;
|
|
497
|
+
bytesDropped: number;
|
|
498
|
+
lastKeepaliveAt?: string;
|
|
499
|
+
keepalivesReceived: number;
|
|
500
|
+
rateLimitBytesPerSec?: number;
|
|
501
|
+
burstBytes?: number;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// Per-client telemetry
|
|
505
|
+
interface IVpnClientTelemetry {
|
|
506
|
+
clientId: string;
|
|
507
|
+
assignedIp: string;
|
|
508
|
+
lastKeepaliveAt?: string;
|
|
509
|
+
keepalivesReceived: number;
|
|
510
|
+
packetsDropped: number;
|
|
511
|
+
bytesDropped: number;
|
|
512
|
+
bytesReceived: number;
|
|
513
|
+
bytesSent: number;
|
|
514
|
+
rateLimitBytesPerSec?: number;
|
|
515
|
+
burstBytes?: number;
|
|
381
516
|
}
|
|
382
517
|
|
|
383
518
|
interface IVpnKeypair {
|