@push.rocks/smartvpn 2.1.0 → 2.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.
- package/assets/third-party-licenses.md +36 -2
- package/dist_rust/smartvpn_daemon_linux_amd64_musl +0 -0
- package/dist_rust/smartvpn_daemon_linux_amd64_musl.tsrust-build.json +4 -4
- package/dist_rust/smartvpn_daemon_linux_arm64_musl +0 -0
- package/dist_rust/smartvpn_daemon_linux_arm64_musl.tsrust-build.json +4 -4
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/smartvpn.classes.vpnbridge.js +15 -2
- package/dist_ts/smartvpn.classes.vpnclient.d.ts +32 -5
- package/dist_ts/smartvpn.classes.vpnclient.js +121 -3
- package/dist_ts/smartvpn.interfaces.d.ts +138 -2
- package/notices/cargo-dependencies.html +32 -1
- package/notices/inventory.json +7 -7
- package/package.json +3 -3
- package/readme.md +104 -0
- package/third-party-notices.md +7 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/smartvpn.classes.vpnbridge.ts +17 -2
- package/ts/smartvpn.classes.vpnclient.ts +141 -3
- package/ts/smartvpn.interfaces.ts +108 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Third-party notices: managed Linux routing
|
|
2
2
|
|
|
3
|
-
The Linux daemon links
|
|
3
|
+
The Linux daemon links these routing dependencies. The original netlink-proto
|
|
4
|
+
0.13.0 attribution is retained alongside its published 0.13.1 fork. Their
|
|
4
5
|
exact upstream LICENSE-MIT texts are reproduced below. This file is distributed
|
|
5
6
|
under package.json's assets/**/* inclusion.
|
|
6
7
|
|
|
@@ -92,7 +93,40 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
92
93
|
SOFTWARE.
|
|
93
94
|
```
|
|
94
95
|
|
|
95
|
-
## netlink-proto 0.13.
|
|
96
|
+
## netlink-proto 0.13.1 (published fork)
|
|
97
|
+
|
|
98
|
+
Source: https://code.foss.global/push.rocks/netlink-proto/src/commit/bcbbfbba7485857c3b1f9fd5f6d99d597a334ab0
|
|
99
|
+
|
|
100
|
+
```text
|
|
101
|
+
Copyright (c) The netlink-proto Contributors
|
|
102
|
+
|
|
103
|
+
Permission is hereby granted, free of charge, to any
|
|
104
|
+
person obtaining a copy of this software and associated
|
|
105
|
+
documentation files (the "Software"), to deal in the
|
|
106
|
+
Software without restriction, including without
|
|
107
|
+
limitation the rights to use, copy, modify, merge,
|
|
108
|
+
publish, distribute, sublicense, and/or sell copies of
|
|
109
|
+
the Software, and to permit persons to whom the Software
|
|
110
|
+
is furnished to do so, subject to the following
|
|
111
|
+
conditions:
|
|
112
|
+
|
|
113
|
+
The above copyright notice and this permission notice
|
|
114
|
+
shall be included in all copies or substantial portions
|
|
115
|
+
of the Software.
|
|
116
|
+
|
|
117
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
118
|
+
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
119
|
+
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
120
|
+
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
|
121
|
+
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
122
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
123
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
|
124
|
+
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
125
|
+
DEALINGS IN THE SOFTWARE.
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## netlink-proto 0.13.0 (upstream attribution retained)
|
|
96
130
|
|
|
97
131
|
Source: https://crates.io/crates/netlink-proto/0.13.0
|
|
98
132
|
|
|
Binary file
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"format": "tsrust.build-provenance.v2",
|
|
3
|
-
"binarySha256": "
|
|
3
|
+
"binarySha256": "bb4eeb71f5b13b197999ea809e05876625df2c93b7600fdfbe56db10970e6589",
|
|
4
4
|
"buildInfo": {
|
|
5
5
|
"projectName": "@push.rocks/smartvpn",
|
|
6
|
-
"projectVersion": "2.
|
|
7
|
-
"gitCommit": "
|
|
6
|
+
"projectVersion": "2.3.0",
|
|
7
|
+
"gitCommit": "4fd05874f4792ba89c6b0fa6cb168434e5c9ee25",
|
|
8
8
|
"gitDirty": false,
|
|
9
|
-
"builtAt": "2026-09-
|
|
9
|
+
"builtAt": "2026-09-15T09:32:44.556Z",
|
|
10
10
|
"tsrustVersion": "1.13.0",
|
|
11
11
|
"binary": "smartvpn_daemon",
|
|
12
12
|
"target": "linux_amd64_musl"
|
|
Binary file
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"format": "tsrust.build-provenance.v2",
|
|
3
|
-
"binarySha256": "
|
|
3
|
+
"binarySha256": "37bc9e0af746d3f799753b605fd6b025cfc3f83b3f225e6bece5ab8ec167f98d",
|
|
4
4
|
"buildInfo": {
|
|
5
5
|
"projectName": "@push.rocks/smartvpn",
|
|
6
|
-
"projectVersion": "2.
|
|
7
|
-
"gitCommit": "
|
|
6
|
+
"projectVersion": "2.3.0",
|
|
7
|
+
"gitCommit": "4fd05874f4792ba89c6b0fa6cb168434e5c9ee25",
|
|
8
8
|
"gitDirty": false,
|
|
9
|
-
"builtAt": "2026-09-
|
|
9
|
+
"builtAt": "2026-09-15T09:32:44.845Z",
|
|
10
10
|
"tsrustVersion": "1.13.0",
|
|
11
11
|
"binary": "smartvpn_daemon",
|
|
12
12
|
"target": "linux_arm64_musl"
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartvpn',
|
|
6
|
-
version: '2.
|
|
6
|
+
version: '2.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=
|
|
@@ -17,13 +17,17 @@ export class VpnBridge extends plugins.events.EventEmitter {
|
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
const binaryName = options.binaryName || 'smartvpn_daemon';
|
|
20
|
+
if (options.transport.transport === 'stdio' && options.transport.binaryPath !== undefined
|
|
21
|
+
&& (typeof options.transport.binaryPath !== 'string' || !plugins.path.isAbsolute(options.transport.binaryPath))) {
|
|
22
|
+
throw new Error('VpnBridge binaryPath must be an absolute executable path');
|
|
23
|
+
}
|
|
20
24
|
// Keep the validated process transport stable even if the caller edits its options.
|
|
21
25
|
this.transportOptions = { ...options.transport };
|
|
22
26
|
this.mode = options.mode;
|
|
23
27
|
this.bridge = new plugins.smartrust.RustBridge({
|
|
24
28
|
binaryName,
|
|
25
29
|
binaryPath: options.transport.transport === 'stdio'
|
|
26
|
-
? paths.getBinaryPath(binaryName)
|
|
30
|
+
? options.transport.binaryPath ?? paths.getBinaryPath(binaryName)
|
|
27
31
|
: undefined,
|
|
28
32
|
localPaths: [],
|
|
29
33
|
searchSystemPath: false,
|
|
@@ -33,6 +37,9 @@ export class VpnBridge extends plugins.events.EventEmitter {
|
|
|
33
37
|
maxPayloadSize: 10 * 1024 * 1024, // 10 MB
|
|
34
38
|
});
|
|
35
39
|
// Forward events from inner bridge
|
|
40
|
+
this.bridge.on('disconnected', (event) => {
|
|
41
|
+
this.emit('disconnected', event);
|
|
42
|
+
});
|
|
36
43
|
this.bridge.on('exit', (code, signal) => {
|
|
37
44
|
this.emit('exit', code, signal);
|
|
38
45
|
});
|
|
@@ -44,6 +51,12 @@ export class VpnBridge extends plugins.events.EventEmitter {
|
|
|
44
51
|
});
|
|
45
52
|
// Forward management events from the daemon
|
|
46
53
|
// smartrust emits 'management:<eventName>' for unsolicited events
|
|
54
|
+
this.bridge.on('management:managed-runtime-invalidated', (data) => {
|
|
55
|
+
this.emit('managed-runtime-invalidated', data);
|
|
56
|
+
});
|
|
57
|
+
this.bridge.on('management:managed-session-state', (data) => {
|
|
58
|
+
this.emit('managed-session-state', data);
|
|
59
|
+
});
|
|
47
60
|
this.bridge.on('management:status', (data) => {
|
|
48
61
|
this.emit('status', data);
|
|
49
62
|
});
|
|
@@ -100,4 +113,4 @@ export class VpnBridge extends plugins.events.EventEmitter {
|
|
|
100
113
|
return this.bridge.running;
|
|
101
114
|
}
|
|
102
115
|
}
|
|
103
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
116
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnR2cG4uY2xhc3Nlcy52cG5icmlkZ2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydHZwbi5jbGFzc2VzLnZwbmJyaWRnZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHVCQUF1QixDQUFDO0FBQ2pELE9BQU8sS0FBSyxLQUFLLE1BQU0scUJBQXFCLENBQUM7QUFTN0M7OztHQUdHO0FBQ0gsTUFBTSxPQUFPLFNBQXlDLFNBQVEsT0FBTyxDQUFDLE1BQU0sQ0FBQyxZQUFZO0lBS3ZGLFlBQVksT0FLWDtRQUNDLEtBQUssRUFBRSxDQUFDO1FBRVIsTUFBTSxXQUFXLEdBQUcsT0FBTyxDQUFDLGtCQUFrQixDQUFDO1FBQy9DLElBQUksV0FBVyxLQUFLLFNBQVMsRUFBRSxDQUFDO1lBQzlCLElBQUksT0FBTyxDQUFDLEVBQUUsQ0FBQyxRQUFRLEVBQUUsS0FBSyxPQUFPLElBQUksT0FBTyxDQUFDLElBQUksS0FBSyxRQUFRLElBQUksT0FBTyxDQUFDLFNBQVMsQ0FBQyxTQUFTLEtBQUssT0FBTyxFQUFFLENBQUM7Z0JBQzlHLE1BQU0sSUFBSSxLQUFLLENBQUMsNERBQTRELENBQUMsQ0FBQztZQUNoRixDQUFDO1lBQ0QsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsV0FBVyxDQUFDLElBQUksV0FBVyxHQUFHLENBQUMsSUFBSSxXQUFXLEdBQUcsV0FBVyxFQUFFLENBQUM7Z0JBQ25GLE1BQU0sSUFBSSxLQUFLLENBQUMsdUVBQXVFLENBQUMsQ0FBQztZQUMzRixDQUFDO1FBQ0gsQ0FBQztRQUNELE1BQU0sVUFBVSxHQUFHLE9BQU8sQ0FBQyxVQUFVLElBQUksaUJBQWlCLENBQUM7UUFDM0QsSUFBSSxPQUFPLENBQUMsU0FBUyxDQUFDLFNBQVMsS0FBSyxPQUFPLElBQUksT0FBTyxDQUFDLFNBQVMsQ0FBQyxVQUFVLEtBQUssU0FBUztlQUNwRixDQUFDLE9BQU8sT0FBTyxD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
|
|
@@ -1,12 +1,37 @@
|
|
|
1
1
|
import * as plugins from './smartvpn.plugins.js';
|
|
2
|
-
import type { IVpnClientOptions, IVpnClientConfig, IVpnStatus, IVpnStatistics, IVpnConnectionQuality, IVpnMtuInfo } from './smartvpn.interfaces.js';
|
|
2
|
+
import type { IVpnClientOptions, IVpnClientConfig, IVpnStatus, IVpnStatistics, IVpnConnectionQuality, IVpnMtuInfo, IVpnConnectionResult, IVpnManagedRuntime, IVpnManagedSessionConfig, IVpnManagedSession } from './smartvpn.interfaces.js';
|
|
3
3
|
/**
|
|
4
4
|
* VPN Client — manages a smartvpn daemon in client mode.
|
|
5
5
|
*/
|
|
6
6
|
export declare class VpnClient extends plugins.events.EventEmitter {
|
|
7
7
|
private bridge;
|
|
8
8
|
private options;
|
|
9
|
+
private bridgeEpoch;
|
|
10
|
+
private managedGeneration?;
|
|
11
|
+
private lastInvalidatedGeneration?;
|
|
12
|
+
private sessionGeneration?;
|
|
13
|
+
private stoppedSessionGeneration?;
|
|
14
|
+
private retiredSessionGeneration?;
|
|
15
|
+
private invalidatedSessionGeneration?;
|
|
9
16
|
constructor(options: IVpnClientOptions);
|
|
17
|
+
private observeSessionEvent;
|
|
18
|
+
private invalidateGeneration;
|
|
19
|
+
private invalidateBridge;
|
|
20
|
+
private checkSession;
|
|
21
|
+
/** Authenticate and retain transport/keepalive without any TUN or route effect. */
|
|
22
|
+
authenticateManaged(config: IVpnManagedSessionConfig): Promise<IVpnManagedSession>;
|
|
23
|
+
/** Create the planned exclusive /32 TUN and split routes with forwarding disabled.
|
|
24
|
+
* Caller must authorize these exact native transitions before calling. */
|
|
25
|
+
prepareManagedTunnel(generation: string): Promise<IVpnManagedSession>;
|
|
26
|
+
/** Reinspect, discard ready pre-enable inputs, then start forwarding. Call only
|
|
27
|
+
* after caller-owned policy is installed and verified against the prepared TUN. */
|
|
28
|
+
enableManagedForwarding(generation: string): Promise<IVpnManagedSession>;
|
|
29
|
+
/** Terminally stop packet I/O and retain the TUN for caller-authorized teardown.
|
|
30
|
+
* This generation cannot forward again. Disconnect then closes its kernel owner. */
|
|
31
|
+
stopManagedForwarding(generation: string): Promise<IVpnManagedSession>;
|
|
32
|
+
inspectManagedSession(generation: string): Promise<IVpnManagedSession>;
|
|
33
|
+
private sessionCommand;
|
|
34
|
+
private checkObservation;
|
|
10
35
|
/**
|
|
11
36
|
* Start the daemon bridge (spawn or connect).
|
|
12
37
|
*/
|
|
@@ -14,9 +39,10 @@ export declare class VpnClient extends plugins.events.EventEmitter {
|
|
|
14
39
|
/**
|
|
15
40
|
* Connect to the VPN server using the provided config.
|
|
16
41
|
*/
|
|
17
|
-
connect(config?: IVpnClientConfig): Promise<
|
|
18
|
-
|
|
19
|
-
|
|
42
|
+
connect(config?: IVpnClientConfig): Promise<IVpnConnectionResult>;
|
|
43
|
+
/** Inspect the retained native TUN, its exact IPv4 /32 and split routes.
|
|
44
|
+
* Requires the connection's generation and fails after retirement/reconnect. */
|
|
45
|
+
inspectManagedRuntime(generation: string): Promise<IVpnManagedRuntime>;
|
|
20
46
|
/**
|
|
21
47
|
* Disconnect from the VPN server.
|
|
22
48
|
*/
|
|
@@ -39,7 +65,8 @@ export declare class VpnClient extends plugins.events.EventEmitter {
|
|
|
39
65
|
*/
|
|
40
66
|
getMtuInfo(): Promise<IVpnMtuInfo | null>;
|
|
41
67
|
/**
|
|
42
|
-
* Stop
|
|
68
|
+
* Stop and join an owned stdio daemon. Socket mode only closes the bridge;
|
|
69
|
+
* disconnect() must first join the independently owned native session.
|
|
43
70
|
*/
|
|
44
71
|
stop(): Promise<void>;
|
|
45
72
|
/**
|
|
@@ -6,6 +6,7 @@ import { VpnBridge } from './smartvpn.classes.vpnbridge.js';
|
|
|
6
6
|
export class VpnClient extends plugins.events.EventEmitter {
|
|
7
7
|
constructor(options) {
|
|
8
8
|
super();
|
|
9
|
+
this.bridgeEpoch = 0;
|
|
9
10
|
this.options = options;
|
|
10
11
|
this.bridge = new VpnBridge({
|
|
11
12
|
transport: options.transport,
|
|
@@ -13,15 +14,113 @@ export class VpnClient extends plugins.events.EventEmitter {
|
|
|
13
14
|
networkNamespaceFd: options.networkNamespaceFd,
|
|
14
15
|
});
|
|
15
16
|
// Forward bridge events
|
|
17
|
+
this.bridge.on('disconnected', () => {
|
|
18
|
+
this.invalidateBridge('Client transport disconnected');
|
|
19
|
+
});
|
|
16
20
|
this.bridge.on('exit', (code, signal) => {
|
|
21
|
+
this.invalidateBridge('Client bridge exited');
|
|
17
22
|
this.emit('exit', { code, signal });
|
|
18
23
|
});
|
|
19
24
|
this.bridge.on('reconnected', () => {
|
|
25
|
+
this.invalidateBridge('Client bridge reconnected');
|
|
20
26
|
this.emit('reconnected');
|
|
21
27
|
});
|
|
22
28
|
this.bridge.on('stderr', (line) => {
|
|
23
29
|
this.emit('stderr', line);
|
|
24
30
|
});
|
|
31
|
+
this.bridge.on('managed-runtime-invalidated', (event) => {
|
|
32
|
+
this.invalidateGeneration(event);
|
|
33
|
+
});
|
|
34
|
+
this.bridge.on('managed-session-state', (event) => {
|
|
35
|
+
this.observeSessionEvent(event);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
observeSessionEvent(event) {
|
|
39
|
+
if (event.phase === 'retired') {
|
|
40
|
+
this.retiredSessionGeneration = event.generation;
|
|
41
|
+
if (this.sessionGeneration === event.generation)
|
|
42
|
+
this.sessionGeneration = undefined;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
this.stoppedSessionGeneration = event.generation;
|
|
46
|
+
}
|
|
47
|
+
this.emit('managed-session-state', event);
|
|
48
|
+
}
|
|
49
|
+
invalidateGeneration(event) {
|
|
50
|
+
if (this.lastInvalidatedGeneration === event.generation)
|
|
51
|
+
return;
|
|
52
|
+
this.lastInvalidatedGeneration = event.generation;
|
|
53
|
+
if (this.managedGeneration === event.generation)
|
|
54
|
+
this.managedGeneration = undefined;
|
|
55
|
+
this.emit('managed-runtime-invalidated', event);
|
|
56
|
+
}
|
|
57
|
+
invalidateBridge(reason) {
|
|
58
|
+
this.bridgeEpoch++;
|
|
59
|
+
if (this.managedGeneration)
|
|
60
|
+
this.invalidateGeneration({ generation: this.managedGeneration, reason });
|
|
61
|
+
if (this.sessionGeneration) {
|
|
62
|
+
const generation = this.sessionGeneration;
|
|
63
|
+
this.invalidatedSessionGeneration = generation;
|
|
64
|
+
this.sessionGeneration = undefined;
|
|
65
|
+
this.emit('managed-session-invalidated', { generation, reason });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
checkSession(result, epoch, generation) {
|
|
69
|
+
const observed = result.plan.generation;
|
|
70
|
+
if (epoch !== this.bridgeEpoch || observed === this.retiredSessionGeneration
|
|
71
|
+
|| observed === this.invalidatedSessionGeneration
|
|
72
|
+
|| (generation !== undefined && observed !== generation)
|
|
73
|
+
|| (observed === this.stoppedSessionGeneration && result.phase !== 'stopped')) {
|
|
74
|
+
throw new Error('Managed session stopped, retired, or lost observation during the operation');
|
|
75
|
+
}
|
|
76
|
+
if (result.runtime) {
|
|
77
|
+
this.checkObservation(result.runtime, epoch, observed);
|
|
78
|
+
this.managedGeneration = observed;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** Authenticate and retain transport/keepalive without any TUN or route effect. */
|
|
82
|
+
async authenticateManaged(config) {
|
|
83
|
+
const epoch = this.bridgeEpoch;
|
|
84
|
+
const result = await this.bridge.sendCommand('authenticateManaged', { config });
|
|
85
|
+
this.checkSession(result, epoch);
|
|
86
|
+
this.sessionGeneration = result.plan.generation;
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
/** Create the planned exclusive /32 TUN and split routes with forwarding disabled.
|
|
90
|
+
* Caller must authorize these exact native transitions before calling. */
|
|
91
|
+
prepareManagedTunnel(generation) {
|
|
92
|
+
return this.sessionCommand('prepareManagedTunnel', generation);
|
|
93
|
+
}
|
|
94
|
+
/** Reinspect, discard ready pre-enable inputs, then start forwarding. Call only
|
|
95
|
+
* after caller-owned policy is installed and verified against the prepared TUN. */
|
|
96
|
+
enableManagedForwarding(generation) {
|
|
97
|
+
return this.sessionCommand('enableManagedForwarding', generation);
|
|
98
|
+
}
|
|
99
|
+
/** Terminally stop packet I/O and retain the TUN for caller-authorized teardown.
|
|
100
|
+
* This generation cannot forward again. Disconnect then closes its kernel owner. */
|
|
101
|
+
stopManagedForwarding(generation) {
|
|
102
|
+
return this.sessionCommand('stopManagedForwarding', generation);
|
|
103
|
+
}
|
|
104
|
+
inspectManagedSession(generation) {
|
|
105
|
+
return this.sessionCommand('inspectManagedSession', generation);
|
|
106
|
+
}
|
|
107
|
+
async sessionCommand(method, generation) {
|
|
108
|
+
if (typeof generation !== 'string' || !/^[a-f0-9]{32}$/.test(generation)) {
|
|
109
|
+
throw new Error('Managed session generation must be a native 128-bit identifier');
|
|
110
|
+
}
|
|
111
|
+
if (generation === this.invalidatedSessionGeneration || generation === this.retiredSessionGeneration) {
|
|
112
|
+
throw new Error('Managed session observation is unavailable');
|
|
113
|
+
}
|
|
114
|
+
const epoch = this.bridgeEpoch;
|
|
115
|
+
const result = await this.bridge.sendCommand(method, { generation });
|
|
116
|
+
this.checkSession(result, epoch, generation);
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
checkObservation(runtime, epoch, generation) {
|
|
120
|
+
if (epoch !== this.bridgeEpoch || runtime.generation === this.lastInvalidatedGeneration
|
|
121
|
+
|| (generation !== undefined && runtime.generation !== generation)) {
|
|
122
|
+
throw new Error('Managed runtime retired during the operation');
|
|
123
|
+
}
|
|
25
124
|
}
|
|
26
125
|
/**
|
|
27
126
|
* Start the daemon bridge (spawn or connect).
|
|
@@ -37,7 +136,24 @@ export class VpnClient extends plugins.events.EventEmitter {
|
|
|
37
136
|
if (!cfg) {
|
|
38
137
|
throw new Error('VpnClient.connect: no config provided');
|
|
39
138
|
}
|
|
40
|
-
|
|
139
|
+
const epoch = this.bridgeEpoch;
|
|
140
|
+
const result = await this.bridge.sendCommand('connect', { config: cfg });
|
|
141
|
+
if (result.managedRuntime) {
|
|
142
|
+
this.checkObservation(result.managedRuntime, epoch);
|
|
143
|
+
this.managedGeneration = result.managedRuntime.generation;
|
|
144
|
+
}
|
|
145
|
+
return result;
|
|
146
|
+
}
|
|
147
|
+
/** Inspect the retained native TUN, its exact IPv4 /32 and split routes.
|
|
148
|
+
* Requires the connection's generation and fails after retirement/reconnect. */
|
|
149
|
+
async inspectManagedRuntime(generation) {
|
|
150
|
+
if (typeof generation !== 'string' || !/^[a-f0-9]{32}$/.test(generation)) {
|
|
151
|
+
throw new Error('Managed runtime generation must be a native 128-bit identifier');
|
|
152
|
+
}
|
|
153
|
+
const epoch = this.bridgeEpoch;
|
|
154
|
+
const result = await this.bridge.sendCommand('inspectManagedRuntime', { generation });
|
|
155
|
+
this.checkObservation(result, epoch, generation);
|
|
156
|
+
return result;
|
|
41
157
|
}
|
|
42
158
|
/**
|
|
43
159
|
* Disconnect from the VPN server.
|
|
@@ -71,9 +187,11 @@ export class VpnClient extends plugins.events.EventEmitter {
|
|
|
71
187
|
return this.bridge.sendCommand('getMtuInfo', {});
|
|
72
188
|
}
|
|
73
189
|
/**
|
|
74
|
-
* Stop
|
|
190
|
+
* Stop and join an owned stdio daemon. Socket mode only closes the bridge;
|
|
191
|
+
* disconnect() must first join the independently owned native session.
|
|
75
192
|
*/
|
|
76
193
|
async stop() {
|
|
194
|
+
this.invalidateBridge('Client stop requested');
|
|
77
195
|
await this.bridge.stop();
|
|
78
196
|
}
|
|
79
197
|
/**
|
|
@@ -83,4 +201,4 @@ export class VpnClient extends plugins.events.EventEmitter {
|
|
|
83
201
|
return this.bridge.running;
|
|
84
202
|
}
|
|
85
203
|
}
|
|
86
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
204
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnR2cG4uY2xhc3Nlcy52cG5jbGllbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydHZwbi5jbGFzc2VzLnZwbmNsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHVCQUF1QixDQUFDO0FBQ2pELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQWlCNUQ7O0dBRUc7QUFDSCxNQUFNLE9BQU8sU0FBVSxTQUFRLE9BQU8sQ0FBQyxNQUFNLENBQUMsWUFBWTtJQVd4RCxZQUFZLE9BQTBCO1FBQ3BDLEtBQUssRUFBRSxDQUFDO1FBVEYsZ0JBQVcsR0FBRyxDQUFDLENBQUM7UUFVdEIsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7UUFDdkIsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLFNBQVMsQ0FBcUI7WUFDOUMsU0FBUyxFQUFFLE9BQU8sQ0FBQyxTQUFTO1lBQzVCLElBQUksRUFBRSxRQUFRO1lBQ2Qsa0JBQWtCLEVBQUUsT0FBTyxDQUFDLGtCQUFrQjtTQUMvQyxDQUFDLENBQUM7UUFFSCx3QkFBd0I7UUFDeEIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsY0FBYyxFQUFFLEdBQUcsRUFBRTtZQUNsQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsK0JBQStCLENBQUMsQ0FBQztRQUN6RCxDQUFDLENBQUMsQ0FBQztRQUNILElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxDQUFDLElBQW1CLEVBQUUsTUFBcUIsRUFBRSxFQUFFO1lBQ3BFLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO1lBQzlDLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUM7UUFDdEMsQ0FBQyxDQUFDLENBQUM7UUFDSCxJQUFJLENBQUMsTUFBTSxDQUFD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
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export interface IVpnTransportStdio {
|
|
2
2
|
transport: 'stdio';
|
|
3
|
+
/** Exact caller-verified executable. Overrides environment/package lookup;
|
|
4
|
+
* invalid paths fail without falling through to another executable. */
|
|
5
|
+
binaryPath?: string;
|
|
3
6
|
}
|
|
4
7
|
export interface IVpnTransportSocket {
|
|
5
8
|
transport: 'socket';
|
|
@@ -404,13 +407,47 @@ export interface IWgPeerInfo {
|
|
|
404
407
|
lastHandshakeTime?: string;
|
|
405
408
|
}
|
|
406
409
|
export type TVpnClientCommands = {
|
|
410
|
+
authenticateManaged: {
|
|
411
|
+
params: {
|
|
412
|
+
config: IVpnManagedSessionConfig;
|
|
413
|
+
};
|
|
414
|
+
result: IVpnManagedSession;
|
|
415
|
+
};
|
|
416
|
+
prepareManagedTunnel: {
|
|
417
|
+
params: {
|
|
418
|
+
generation: string;
|
|
419
|
+
};
|
|
420
|
+
result: IVpnManagedSession;
|
|
421
|
+
};
|
|
422
|
+
enableManagedForwarding: {
|
|
423
|
+
params: {
|
|
424
|
+
generation: string;
|
|
425
|
+
};
|
|
426
|
+
result: IVpnManagedSession;
|
|
427
|
+
};
|
|
428
|
+
stopManagedForwarding: {
|
|
429
|
+
params: {
|
|
430
|
+
generation: string;
|
|
431
|
+
};
|
|
432
|
+
result: IVpnManagedSession;
|
|
433
|
+
};
|
|
434
|
+
inspectManagedSession: {
|
|
435
|
+
params: {
|
|
436
|
+
generation: string;
|
|
437
|
+
};
|
|
438
|
+
result: IVpnManagedSession;
|
|
439
|
+
};
|
|
407
440
|
connect: {
|
|
408
441
|
params: {
|
|
409
442
|
config: IVpnClientConfig;
|
|
410
443
|
};
|
|
411
|
-
result:
|
|
412
|
-
|
|
444
|
+
result: IVpnConnectionResult;
|
|
445
|
+
};
|
|
446
|
+
inspectManagedRuntime: {
|
|
447
|
+
params: {
|
|
448
|
+
generation: string;
|
|
413
449
|
};
|
|
450
|
+
result: IVpnManagedRuntime;
|
|
414
451
|
};
|
|
415
452
|
disconnect: {
|
|
416
453
|
params: Record<string, never>;
|
|
@@ -649,6 +686,102 @@ export interface IManagedNetworkBinding {
|
|
|
649
686
|
authorityId: string;
|
|
650
687
|
nodeId: string;
|
|
651
688
|
}
|
|
689
|
+
export interface IVpnConnectionResult {
|
|
690
|
+
assignedIp: string;
|
|
691
|
+
/** Present only for an authenticated native managed connection with a real TUN. */
|
|
692
|
+
managedRuntime?: IVpnManagedRuntime;
|
|
693
|
+
}
|
|
694
|
+
/** Linux native client with explicit kernel-effect stages. The caller supplies
|
|
695
|
+
* a conservative deadline from its independently qualified clock authority. */
|
|
696
|
+
export interface IVpnManagedSessionConfig {
|
|
697
|
+
serverUrl: string;
|
|
698
|
+
serverPublicKey: string;
|
|
699
|
+
clientPrivateKey: string;
|
|
700
|
+
clientPublicKey: string;
|
|
701
|
+
transport: 'quic' | 'websocket';
|
|
702
|
+
serverCertHash?: string;
|
|
703
|
+
mtu?: number;
|
|
704
|
+
managedNetwork: IManagedNetworkBinding;
|
|
705
|
+
/** Same native boot; deadline must be future and at most fifteen minutes away.
|
|
706
|
+
* Expiry stops packet I/O even across suspend. It does not delete a held TUN. */
|
|
707
|
+
lease: {
|
|
708
|
+
bootId: string;
|
|
709
|
+
deadlineBoottimeMs: number;
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
/** Authentication returns the full validated assignment and planned exclusive
|
|
713
|
+
* TUN name before creating a device or route. Kernel policy remains caller-owned. */
|
|
714
|
+
export interface IVpnManagedSessionPlan {
|
|
715
|
+
schemaVersion: 1;
|
|
716
|
+
generation: string;
|
|
717
|
+
namespace: {
|
|
718
|
+
device: string;
|
|
719
|
+
inode: string;
|
|
720
|
+
};
|
|
721
|
+
interfaceName: string;
|
|
722
|
+
assignment: {
|
|
723
|
+
assignedIp: string;
|
|
724
|
+
gateway: string;
|
|
725
|
+
subnet: string;
|
|
726
|
+
mtu: number;
|
|
727
|
+
effectiveMtu: number;
|
|
728
|
+
managed: IManagedNetworkAssignment;
|
|
729
|
+
};
|
|
730
|
+
mtu: number;
|
|
731
|
+
remoteIp: string;
|
|
732
|
+
lease: IVpnManagedSessionConfig['lease'];
|
|
733
|
+
}
|
|
734
|
+
export interface IVpnManagedSession {
|
|
735
|
+
plan: IVpnManagedSessionPlan;
|
|
736
|
+
phase: 'authenticated' | 'prepared' | 'forwarding' | 'stopped';
|
|
737
|
+
/** Fresh native inspection when present. Prepared TUN is UP with forwarding
|
|
738
|
+
* disabled. A stopped TUN stays owned until disconnect() joins deletion, or an
|
|
739
|
+
* owned stdio process is joined by stop(). Socket stop() does not delete it. */
|
|
740
|
+
runtime: IVpnManagedRuntime | null;
|
|
741
|
+
}
|
|
742
|
+
/** Stopped means the complete packet I/O future ended; retired means the native
|
|
743
|
+
* session owner ended. Neither proves namespace-wide cleanup, conntrack drain,
|
|
744
|
+
* packets already queued on the network, or safe allocation reuse. */
|
|
745
|
+
export interface IVpnManagedSessionEvent {
|
|
746
|
+
generation: string;
|
|
747
|
+
phase: 'stopped' | 'retired';
|
|
748
|
+
reason: string;
|
|
749
|
+
}
|
|
750
|
+
/** The facade lost observation of this generation. Native packet I/O and TUN
|
|
751
|
+
* ownership may remain alive, especially with an independent socket daemon.
|
|
752
|
+
* This is neither a stopped nor a retired native lifecycle acknowledgement. */
|
|
753
|
+
export interface IVpnManagedSessionInvalidation {
|
|
754
|
+
generation: string;
|
|
755
|
+
reason: string;
|
|
756
|
+
}
|
|
757
|
+
/** A fresh observation of a retained native lifetime, never a transferable or
|
|
758
|
+
* restorable capability. The caller retains VpnClient and listens for invalidation.
|
|
759
|
+
* IPv6, namespace-wide policy/rules, underlay and firewall remain caller-owned. */
|
|
760
|
+
export interface IVpnManagedRuntime {
|
|
761
|
+
schemaVersion: 1;
|
|
762
|
+
generation: string;
|
|
763
|
+
namespace: {
|
|
764
|
+
device: string;
|
|
765
|
+
inode: string;
|
|
766
|
+
};
|
|
767
|
+
link: {
|
|
768
|
+
interfaceIndex: number;
|
|
769
|
+
interfaceName: string;
|
|
770
|
+
interfaceKind: 'tun';
|
|
771
|
+
linkIndex: number;
|
|
772
|
+
mtu: number;
|
|
773
|
+
address: string;
|
|
774
|
+
prefixLength: 32;
|
|
775
|
+
};
|
|
776
|
+
remoteIp: string;
|
|
777
|
+
assignment: IManagedNetworkAssignment;
|
|
778
|
+
}
|
|
779
|
+
/** Stop using this generation immediately. This event is not a joined cleanup,
|
|
780
|
+
* conntrack drain, or allocation-reuse acknowledgement. */
|
|
781
|
+
export interface IVpnManagedRuntimeInvalidation {
|
|
782
|
+
generation: string;
|
|
783
|
+
reason: string;
|
|
784
|
+
}
|
|
652
785
|
export interface IManagedNodeConfig {
|
|
653
786
|
/** Caller-owned control address with /32; local workload prefixes are not hub routes. */
|
|
654
787
|
address: string;
|
|
@@ -674,6 +807,9 @@ export interface IVpnServiceUnit {
|
|
|
674
807
|
installPath: string;
|
|
675
808
|
}
|
|
676
809
|
export interface IVpnEventMap {
|
|
810
|
+
'managed-session-state': IVpnManagedSessionEvent;
|
|
811
|
+
'managed-session-invalidated': IVpnManagedSessionInvalidation;
|
|
812
|
+
'managed-runtime-invalidated': IVpnManagedRuntimeInvalidation;
|
|
677
813
|
'status': IVpnStatus;
|
|
678
814
|
'error': {
|
|
679
815
|
message: string;
|
|
@@ -3004,6 +3004,38 @@ The above copyright notice and this permission notice
|
|
|
3004
3004
|
shall be included in all copies or substantial portions
|
|
3005
3005
|
of the Software.
|
|
3006
3006
|
|
|
3007
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
3008
|
+
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
3009
|
+
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
3010
|
+
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
|
3011
|
+
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
3012
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
3013
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
|
3014
|
+
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
3015
|
+
DEALINGS IN THE SOFTWARE.
|
|
3016
|
+
</pre>
|
|
3017
|
+
</section>
|
|
3018
|
+
<section>
|
|
3019
|
+
<h2>MIT License (MIT)</h2>
|
|
3020
|
+
<ul>
|
|
3021
|
+
<li>netlink-proto 0.13.1</li>
|
|
3022
|
+
</ul>
|
|
3023
|
+
<pre>Copyright (c) The netlink-proto Contributors
|
|
3024
|
+
|
|
3025
|
+
Permission is hereby granted, free of charge, to any
|
|
3026
|
+
person obtaining a copy of this software and associated
|
|
3027
|
+
documentation files (the "Software"), to deal in the
|
|
3028
|
+
Software without restriction, including without
|
|
3029
|
+
limitation the rights to use, copy, modify, merge,
|
|
3030
|
+
publish, distribute, sublicense, and/or sell copies of
|
|
3031
|
+
the Software, and to permit persons to whom the Software
|
|
3032
|
+
is furnished to do so, subject to the following
|
|
3033
|
+
conditions:
|
|
3034
|
+
|
|
3035
|
+
The above copyright notice and this permission notice
|
|
3036
|
+
shall be included in all copies or substantial portions
|
|
3037
|
+
of the Software.
|
|
3038
|
+
|
|
3007
3039
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
3008
3040
|
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
3009
3041
|
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
@@ -3610,7 +3642,6 @@ DEALINGS IN THE SOFTWARE.
|
|
|
3610
3642
|
<h2>MIT License (MIT)</h2>
|
|
3611
3643
|
<ul>
|
|
3612
3644
|
<li>netlink-packet-core 0.9.0</li>
|
|
3613
|
-
<li>netlink-proto 0.13.0</li>
|
|
3614
3645
|
<li>netlink-sys 0.9.0</li>
|
|
3615
3646
|
<li>rtnetlink 0.23.0</li>
|
|
3616
3647
|
</ul>
|
package/notices/inventory.json
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
"rustVersion": "1.95.0",
|
|
4
4
|
"cargoAboutVersion": "0.9.1",
|
|
5
5
|
"files": {
|
|
6
|
-
"assets/third-party-licenses.md": "
|
|
6
|
+
"assets/third-party-licenses.md": "6be2373e39312d90eb3571bef614c89df565f98c917795d7aea3497f52ef19f1",
|
|
7
7
|
"notices/aho-corasick-license.txt": "0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f",
|
|
8
|
-
"notices/cargo-dependencies.html": "
|
|
8
|
+
"notices/cargo-dependencies.html": "bd8e78c6f49c0ee60ccf6e1449c9b52276940055b08aaf32c5ede1ec3b307b4c",
|
|
9
9
|
"notices/compiler-builtins-license.txt": "ab6eec6caf0fa5775e411c7a8bc6a45c4ef2956b0980b157ab74fc5cd62a928b",
|
|
10
10
|
"notices/defmt-license.txt": "0d17b75c1867fd568bcbb735f329d0d4253846c4b756a65e4d440c1e4bd59187",
|
|
11
11
|
"notices/llvm-libunwind-license.txt": "b5efebcaca80879234098e52d1725e6d9eb8fb96a19fce625d39184b705f7b6d",
|
|
@@ -16,11 +16,11 @@
|
|
|
16
16
|
"notices/valuable-license.txt": "ed60d479b8fd1f64e9cbc3de449a16a53ac1b3d1b6aeb9bf9d190a8e93061b44",
|
|
17
17
|
"rust-toolchain.toml": "19da9ff32e229a69ff5dde28ac23233256ff25e413a8c579155cf641f4f0634f",
|
|
18
18
|
"rust/.cargo/config.toml": "eb6896791bd573f2cb099aa41c7d3668c6d368166945abd3553c8d8029ba8b7b",
|
|
19
|
-
"rust/Cargo.lock": "
|
|
20
|
-
"rust/Cargo.toml": "
|
|
19
|
+
"rust/Cargo.lock": "93980ffcc3d2eca0083f524999b15f1674698d2aceff5922ed0aa5a09138d260",
|
|
20
|
+
"rust/Cargo.toml": "308d11b4bb6b8f290ca2d83f2c817e10798bad54510dd3a6fdb72c32d095ae3b",
|
|
21
21
|
"rust/about.hbs": "a7c35d089563a08520a6e5f351aadae659e1f7bf24cabedbca3de8a89e966f86",
|
|
22
|
-
"rust/about.toml": "
|
|
23
|
-
"third-party-notices.md": "
|
|
22
|
+
"rust/about.toml": "a457e0cc7532020c0f87532919a754997213ccbec6211c9501b8c198d404ed72",
|
|
23
|
+
"third-party-notices.md": "9b3adc6a4968ba5ef1eb666188ddb147933e2d90bafca040a59789b2f4b9389b"
|
|
24
24
|
},
|
|
25
25
|
"supplementalCargoNotices": {
|
|
26
26
|
"aho-corasick@1.1.4": "notices/aho-corasick-license.txt",
|
|
@@ -605,7 +605,7 @@
|
|
|
605
605
|
},
|
|
606
606
|
{
|
|
607
607
|
"name": "netlink-proto",
|
|
608
|
-
"version": "0.13.
|
|
608
|
+
"version": "0.13.1",
|
|
609
609
|
"license": "MIT"
|
|
610
610
|
},
|
|
611
611
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@push.rocks/smartvpn",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A VPN solution with TypeScript control plane and Rust data plane daemon",
|
|
6
6
|
"type": "module",
|
|
@@ -25,14 +25,14 @@
|
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@push.rocks/smartnftables": "1.2.0",
|
|
27
27
|
"@push.rocks/smartpath": "^6.0.0",
|
|
28
|
-
"@push.rocks/smartrust": "^2.
|
|
28
|
+
"@push.rocks/smartrust": "^2.1.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@git.zone/tsbuild": "^4.4.3",
|
|
32
32
|
"@git.zone/tsrun": "^2.0.6",
|
|
33
33
|
"@git.zone/tsrust": "^1.13.0",
|
|
34
34
|
"@git.zone/tstest": "^6.0.0",
|
|
35
|
-
"@types/node": "^26.
|
|
35
|
+
"@types/node": "^26.5.0"
|
|
36
36
|
},
|
|
37
37
|
"files": [
|
|
38
38
|
"ts/**/*",
|
package/readme.md
CHANGED
|
@@ -155,6 +155,109 @@ managed TUN creation, allow/deny, reconnect, revocation, route-conflict rollback
|
|
|
155
155
|
and child-termination cleanup. Privileged ARM64 and Pallet integration remain
|
|
156
156
|
separate qualification requirements.
|
|
157
157
|
|
|
158
|
+
### Staged managed client activation
|
|
159
|
+
|
|
160
|
+
`authenticateManaged(config)` is a Linux native client API for a caller that must
|
|
161
|
+
install packet policy before forwarding. Its closed configuration accepts an
|
|
162
|
+
explicit `quic` or `websocket` transport, a matching Noise keypair, server public
|
|
163
|
+
key, `managedNetwork` binding, optional MTU/certificate pin and a native lease.
|
|
164
|
+
Testing mode, automatic transport fallback and extra configuration fields reject.
|
|
165
|
+
|
|
166
|
+
The lease is `{ bootId, deadlineBoottimeMs }`: the actual Linux boot ID and an
|
|
167
|
+
absolute `CLOCK_BOOTTIME` deadline, future and no more than fifteen minutes away.
|
|
168
|
+
The caller derives this conservative deadline from its independently qualified
|
|
169
|
+
authority; SmartVPN does not turn wall-clock credentials into trusted time.
|
|
170
|
+
Native expiry interrupts the whole packet I/O future, including blocked writes,
|
|
171
|
+
and remains effective across suspend. Leases cannot be extended in place.
|
|
172
|
+
|
|
173
|
+
Register `managed-session-state`, `managed-session-invalidated` and
|
|
174
|
+
`managed-runtime-invalidated` listeners before
|
|
175
|
+
starting. Keep the live client owner and use the returned generation for each step:
|
|
176
|
+
|
|
177
|
+
1. `authenticateManaged(config)` authenticates Noise and validates the full server
|
|
178
|
+
assignment. It retains transport and keepalive without creating a TUN or route.
|
|
179
|
+
The returned `plan` includes the actual namespace, remote endpoint, authenticated
|
|
180
|
+
assignment, effective MTU and an exclusive planned TUN name.
|
|
181
|
+
2. Authorize the planned native changes in the caller's namespace/policy owner.
|
|
182
|
+
`prepareManagedTunnel(generation)` then creates that fresh nonpersistent TUN
|
|
183
|
+
with `IFF_TUN_EXCL`, the assigned IPv4 /32 and exact split routes. The interface
|
|
184
|
+
is UP; packet forwarding remains disabled. An existing same-named interface
|
|
185
|
+
causes failure and is never adopted or deleted.
|
|
186
|
+
3. Install and verify caller-owned policy against the fresh returned `runtime`.
|
|
187
|
+
`enableManagedForwarding(generation)` reinspects the native inventory, discards
|
|
188
|
+
already-ready incoming encrypted frames and TUN packets, then starts forwarding.
|
|
189
|
+
Incoming ciphertext is consumed while disabled so Noise nonces stay synchronized.
|
|
190
|
+
Continuous ready input can prevent enable from completing; it cannot enable early.
|
|
191
|
+
4. `stopManagedForwarding(generation)` terminally ends packet I/O while retaining
|
|
192
|
+
the TUN and its monitor. A stopped generation cannot restart. Fence caller-owned
|
|
193
|
+
policy, authorize deletion, then `disconnect()` to close and join the kernel owner.
|
|
194
|
+
`stop()` additionally closes an owned stdio process; socket transport leaves the
|
|
195
|
+
independently owned daemon alive and requires `disconnect()` for native cleanup.
|
|
196
|
+
|
|
197
|
+
`inspectManagedSession(generation)` returns the current phase and a fresh TUN audit
|
|
198
|
+
when a device exists. Prepare/enable repeats inspect the same live generation;
|
|
199
|
+
stale generations reject before effects. A lost reply or timeout does not prove
|
|
200
|
+
that a native transition had no effect: inspect the retained owner and reconcile
|
|
201
|
+
the caller's independent inventory before proceeding. Never use serialized receipts
|
|
202
|
+
to recreate ownership or silently reconnect into an old policy epoch.
|
|
203
|
+
|
|
204
|
+
Transport loss and lease expiry emit a `managed-session-state` event with phase
|
|
205
|
+
`stopped` after packet I/O ends, retaining the kernel device for explicit teardown.
|
|
206
|
+
Changed or restored kernel inventory instead retires the entire native owner and
|
|
207
|
+
closes its TUN even while packet I/O is blocked. Phase `retired` follows native
|
|
208
|
+
owner destruction. Bridge loss instead emits `managed-session-invalidated` and
|
|
209
|
+
invalidates facade observations immediately; the native session may still be
|
|
210
|
+
forwarding in an independently owned socket daemon. These events do not prove global
|
|
211
|
+
policy removal, conntrack drainage, delivery of previously queued network packets,
|
|
212
|
+
or safe allocation reuse. IPv6, underlay, workload links, sysctls, global routes and
|
|
213
|
+
firewall policy remain the caller's responsibility.
|
|
214
|
+
|
|
215
|
+
### Retained managed TUN runtime
|
|
216
|
+
|
|
217
|
+
An authenticated native managed connection with `forwardingMode: 'tun'` returns
|
|
218
|
+
`managedRuntime` alongside `assignedIp`. Its random `generation` identifies that
|
|
219
|
+
one connection's retained TUN and netlink owner. The observation includes the
|
|
220
|
+
actual namespace device/inode, TUN name/index/link index, MTU, IPv4 /32, actual hub
|
|
221
|
+
peer IP and authenticated managed assignment. Testing and WireGuard connections
|
|
222
|
+
do not provide this capability.
|
|
223
|
+
|
|
224
|
+
`await client.inspectManagedRuntime(generation)` performs a new bounded kernel
|
|
225
|
+
inspection of that retained owner. It requires complete, uninterrupted link,
|
|
226
|
+
IPv4 address and route inventories, including successful netlink DONE messages.
|
|
227
|
+
A missing, retired or different generation rejects; no serialized observation
|
|
228
|
+
can reopen a namespace, adopt another interface or restore an old connection.
|
|
229
|
+
|
|
230
|
+
Register `client.on('managed-runtime-invalidated', handler)` before connecting.
|
|
231
|
+
Stop admitting work against that generation as soon as the event arrives or an
|
|
232
|
+
inspection/IPC operation fails. The native owner subscribes before its first
|
|
233
|
+
inspection and retires on any observed change to the owned link or IPv4 address,
|
|
234
|
+
its routes, or an equal/more-specific IPv4 route to its managed destinations.
|
|
235
|
+
A change followed by restoration still retires the generation. Protocol closure,
|
|
236
|
+
malformed inventory, interruption and overflow fail closed. Reconnecting creates
|
|
237
|
+
a new generation. This event is also emitted immediately when the facade loses
|
|
238
|
+
its transport, including the gap before automatic socket reconnect, or starts
|
|
239
|
+
stopping. Reattaching to a surviving daemon cannot revive an invalidated generation;
|
|
240
|
+
establish a fresh managed connection. The event does not acknowledge joined cleanup
|
|
241
|
+
or reusable allocations.
|
|
242
|
+
|
|
243
|
+
The native monitor shares the joined packet-task lifetime. On retirement the
|
|
244
|
+
packet future and its nonpersistent TUN close before the native event is emitted;
|
|
245
|
+
the kernel removes the TUN's routes. There is no name-based route cleanup or
|
|
246
|
+
detached monitor. `disconnect()` joins the native connection; `stop()` confirms
|
|
247
|
+
the owned bridge's closure. Socket transport still leaves the separately owned
|
|
248
|
+
daemon process alive, so use `disconnect()` for native connection teardown.
|
|
249
|
+
|
|
250
|
+
These are IPv4 TUN facts, not namespace-wide routing or packet-policy authority.
|
|
251
|
+
The caller owns underlay/DHCP changes, global routing rules, sysctls, IPv6 policy,
|
|
252
|
+
firewall enforcement and workload admission. Normal `getStatus()` is an observed
|
|
253
|
+
connection status and does not substitute for a fresh kernel inspection.
|
|
254
|
+
|
|
255
|
+
For sealed distributions, pass an already verified executable as
|
|
256
|
+
`transport: { transport: 'stdio', binaryPath: '/absolute/path/to/smartvpn_daemon' }`.
|
|
257
|
+
This selects that exact file ahead of environment/package lookup. Invalid paths
|
|
258
|
+
reject without another executable being selected. Namespace descriptors remain
|
|
259
|
+
separate `IVpnClientOptions.networkNamespaceFd` configuration.
|
|
260
|
+
|
|
158
261
|
### Confirmed bridge shutdown
|
|
159
262
|
|
|
160
263
|
Always await `client.stop()`, `server.stop()` or `bridge.stop()` before replacing
|
|
@@ -805,6 +908,7 @@ client.on('exit', ({ code, signal }) => { /* daemon process exited */ });
|
|
|
805
908
|
| `exit` | Both | `{ code, signal }` — daemon process exited |
|
|
806
909
|
| `reconnected` | Both | `void` — socket transport reconnected |
|
|
807
910
|
| `stderr` | Both | `string` — daemon diagnostic line, forwarded without automatic logging |
|
|
911
|
+
| `managed-runtime-invalidated` | Client | `IVpnManagedRuntimeInvalidation` — stop using this managed generation immediately |
|
|
808
912
|
|
|
809
913
|
For connection state and telemetry, use `getStatus()`, `getStatistics()`, `listClients()`, and `getClientTelemetry()`.
|
|
810
914
|
|
package/third-party-notices.md
CHANGED
|
@@ -22,7 +22,7 @@ locked, feature-inactive packages are covered by these supplemental MIT notices:
|
|
|
22
22
|
- [valuable 0.1.1](./notices/valuable-license.txt), from
|
|
23
23
|
[its pinned upstream LICENSE](https://github.com/tokio-rs/valuable/blob/9efc29b6e58cef28f6566a47aa7e142a55fead77/LICENSE).
|
|
24
24
|
|
|
25
|
-
Together these cover all 278 locked
|
|
25
|
+
Together these cover all 278 locked external packages, conservatively including
|
|
26
26
|
build-only and target-inactive dependencies. Listing a package does not mean it
|
|
27
27
|
is linked into either Linux executable. MIT is preferred where offered as an
|
|
28
28
|
alternative; other selected terms include Apache-2.0, BSD, ISC, Unicode-3.0,
|
|
@@ -30,6 +30,12 @@ CDLA-Permissive-2.0 and the existing tun 0.7.22 dependency's WTFPL.
|
|
|
30
30
|
The exact package versions, declared license expressions and reviewed file hashes
|
|
31
31
|
are recorded in [inventory.json](./notices/inventory.json).
|
|
32
32
|
|
|
33
|
+
The managed kernel inventory uses the published `netlink-proto` 0.13.1 fork at
|
|
34
|
+
`bcbbfbba7485857c3b1f9fd5f6d99d597a334ab0` from
|
|
35
|
+
[push.rocks/netlink-proto](https://code.foss.global/push.rocks/netlink-proto).
|
|
36
|
+
Its exact MIT notice is included in the generated inventory and supplemental
|
|
37
|
+
route notices; the original 0.13.0 attribution is retained there as well.
|
|
38
|
+
|
|
33
39
|
The [additional MIT source attributions](./notices/mit-source-attributions.txt)
|
|
34
40
|
retain cesu8 1.1.0's Rust Project/Eric Kidd source headers and siphasher 1.0.2's
|
|
35
41
|
Rust Project/Frank Denis `COPYING` attribution, together with the selected MIT terms.
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,8 +3,10 @@ import * as paths from './smartvpn.paths.js';
|
|
|
3
3
|
import type {
|
|
4
4
|
TVpnTransportOptions,
|
|
5
5
|
IVpnTransportSocket,
|
|
6
|
+
IVpnManagedRuntimeInvalidation,
|
|
7
|
+
IVpnManagedSessionEvent,
|
|
6
8
|
} from './smartvpn.interfaces.js';
|
|
7
|
-
import type { TCommandMap } from '@push.rocks/smartrust';
|
|
9
|
+
import type { TCommandMap, IRustBridgeDisconnectedEvent } from '@push.rocks/smartrust';
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Shared bridge wrapper around smartrust RustBridge.
|
|
@@ -33,6 +35,10 @@ export class VpnBridge<TCommands extends TCommandMap> extends plugins.events.Eve
|
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
37
|
const binaryName = options.binaryName || 'smartvpn_daemon';
|
|
38
|
+
if (options.transport.transport === 'stdio' && options.transport.binaryPath !== undefined
|
|
39
|
+
&& (typeof options.transport.binaryPath !== 'string' || !plugins.path.isAbsolute(options.transport.binaryPath))) {
|
|
40
|
+
throw new Error('VpnBridge binaryPath must be an absolute executable path');
|
|
41
|
+
}
|
|
36
42
|
// Keep the validated process transport stable even if the caller edits its options.
|
|
37
43
|
this.transportOptions = { ...options.transport };
|
|
38
44
|
this.mode = options.mode;
|
|
@@ -40,7 +46,7 @@ export class VpnBridge<TCommands extends TCommandMap> extends plugins.events.Eve
|
|
|
40
46
|
this.bridge = new plugins.smartrust.RustBridge<TCommands>({
|
|
41
47
|
binaryName,
|
|
42
48
|
binaryPath: options.transport.transport === 'stdio'
|
|
43
|
-
? paths.getBinaryPath(binaryName)
|
|
49
|
+
? options.transport.binaryPath ?? paths.getBinaryPath(binaryName)
|
|
44
50
|
: undefined,
|
|
45
51
|
localPaths: [],
|
|
46
52
|
searchSystemPath: false,
|
|
@@ -51,6 +57,9 @@ export class VpnBridge<TCommands extends TCommandMap> extends plugins.events.Eve
|
|
|
51
57
|
});
|
|
52
58
|
|
|
53
59
|
// Forward events from inner bridge
|
|
60
|
+
this.bridge.on('disconnected', (event: IRustBridgeDisconnectedEvent) => {
|
|
61
|
+
this.emit('disconnected', event);
|
|
62
|
+
});
|
|
54
63
|
this.bridge.on('exit', (code: number | null, signal: string | null) => {
|
|
55
64
|
this.emit('exit', code, signal);
|
|
56
65
|
});
|
|
@@ -63,6 +72,12 @@ export class VpnBridge<TCommands extends TCommandMap> extends plugins.events.Eve
|
|
|
63
72
|
|
|
64
73
|
// Forward management events from the daemon
|
|
65
74
|
// smartrust emits 'management:<eventName>' for unsolicited events
|
|
75
|
+
this.bridge.on('management:managed-runtime-invalidated', (data: IVpnManagedRuntimeInvalidation) => {
|
|
76
|
+
this.emit('managed-runtime-invalidated', data);
|
|
77
|
+
});
|
|
78
|
+
this.bridge.on('management:managed-session-state', (data: IVpnManagedSessionEvent) => {
|
|
79
|
+
this.emit('managed-session-state', data);
|
|
80
|
+
});
|
|
66
81
|
this.bridge.on('management:status', (data: any) => {
|
|
67
82
|
this.emit('status', data);
|
|
68
83
|
});
|
|
@@ -7,6 +7,12 @@ import type {
|
|
|
7
7
|
IVpnStatistics,
|
|
8
8
|
IVpnConnectionQuality,
|
|
9
9
|
IVpnMtuInfo,
|
|
10
|
+
IVpnConnectionResult,
|
|
11
|
+
IVpnManagedRuntime,
|
|
12
|
+
IVpnManagedRuntimeInvalidation,
|
|
13
|
+
IVpnManagedSessionConfig,
|
|
14
|
+
IVpnManagedSession,
|
|
15
|
+
IVpnManagedSessionEvent,
|
|
10
16
|
TVpnClientCommands,
|
|
11
17
|
} from './smartvpn.interfaces.js';
|
|
12
18
|
|
|
@@ -16,6 +22,13 @@ import type {
|
|
|
16
22
|
export class VpnClient extends plugins.events.EventEmitter {
|
|
17
23
|
private bridge: VpnBridge<TVpnClientCommands>;
|
|
18
24
|
private options: IVpnClientOptions;
|
|
25
|
+
private bridgeEpoch = 0;
|
|
26
|
+
private managedGeneration?: string;
|
|
27
|
+
private lastInvalidatedGeneration?: string;
|
|
28
|
+
private sessionGeneration?: string;
|
|
29
|
+
private stoppedSessionGeneration?: string;
|
|
30
|
+
private retiredSessionGeneration?: string;
|
|
31
|
+
private invalidatedSessionGeneration?: string;
|
|
19
32
|
|
|
20
33
|
constructor(options: IVpnClientOptions) {
|
|
21
34
|
super();
|
|
@@ -27,15 +40,120 @@ export class VpnClient extends plugins.events.EventEmitter {
|
|
|
27
40
|
});
|
|
28
41
|
|
|
29
42
|
// Forward bridge events
|
|
43
|
+
this.bridge.on('disconnected', () => {
|
|
44
|
+
this.invalidateBridge('Client transport disconnected');
|
|
45
|
+
});
|
|
30
46
|
this.bridge.on('exit', (code: number | null, signal: string | null) => {
|
|
47
|
+
this.invalidateBridge('Client bridge exited');
|
|
31
48
|
this.emit('exit', { code, signal });
|
|
32
49
|
});
|
|
33
50
|
this.bridge.on('reconnected', () => {
|
|
51
|
+
this.invalidateBridge('Client bridge reconnected');
|
|
34
52
|
this.emit('reconnected');
|
|
35
53
|
});
|
|
36
54
|
this.bridge.on('stderr', (line: string) => {
|
|
37
55
|
this.emit('stderr', line);
|
|
38
56
|
});
|
|
57
|
+
this.bridge.on('managed-runtime-invalidated', (event: IVpnManagedRuntimeInvalidation) => {
|
|
58
|
+
this.invalidateGeneration(event);
|
|
59
|
+
});
|
|
60
|
+
this.bridge.on('managed-session-state', (event: IVpnManagedSessionEvent) => {
|
|
61
|
+
this.observeSessionEvent(event);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private observeSessionEvent(event: IVpnManagedSessionEvent): void {
|
|
66
|
+
if (event.phase === 'retired') {
|
|
67
|
+
this.retiredSessionGeneration = event.generation;
|
|
68
|
+
if (this.sessionGeneration === event.generation) this.sessionGeneration = undefined;
|
|
69
|
+
} else {
|
|
70
|
+
this.stoppedSessionGeneration = event.generation;
|
|
71
|
+
}
|
|
72
|
+
this.emit('managed-session-state', event);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private invalidateGeneration(event: IVpnManagedRuntimeInvalidation): void {
|
|
76
|
+
if (this.lastInvalidatedGeneration === event.generation) return;
|
|
77
|
+
this.lastInvalidatedGeneration = event.generation;
|
|
78
|
+
if (this.managedGeneration === event.generation) this.managedGeneration = undefined;
|
|
79
|
+
this.emit('managed-runtime-invalidated', event);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private invalidateBridge(reason: string): void {
|
|
83
|
+
this.bridgeEpoch++;
|
|
84
|
+
if (this.managedGeneration) this.invalidateGeneration({ generation: this.managedGeneration, reason });
|
|
85
|
+
if (this.sessionGeneration) {
|
|
86
|
+
const generation = this.sessionGeneration;
|
|
87
|
+
this.invalidatedSessionGeneration = generation;
|
|
88
|
+
this.sessionGeneration = undefined;
|
|
89
|
+
this.emit('managed-session-invalidated', { generation, reason });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private checkSession(result: IVpnManagedSession, epoch: number, generation?: string): void {
|
|
94
|
+
const observed = result.plan.generation;
|
|
95
|
+
if (epoch !== this.bridgeEpoch || observed === this.retiredSessionGeneration
|
|
96
|
+
|| observed === this.invalidatedSessionGeneration
|
|
97
|
+
|| (generation !== undefined && observed !== generation)
|
|
98
|
+
|| (observed === this.stoppedSessionGeneration && result.phase !== 'stopped')) {
|
|
99
|
+
throw new Error('Managed session stopped, retired, or lost observation during the operation');
|
|
100
|
+
}
|
|
101
|
+
if (result.runtime) {
|
|
102
|
+
this.checkObservation(result.runtime, epoch, observed);
|
|
103
|
+
this.managedGeneration = observed;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Authenticate and retain transport/keepalive without any TUN or route effect. */
|
|
108
|
+
public async authenticateManaged(config: IVpnManagedSessionConfig): Promise<IVpnManagedSession> {
|
|
109
|
+
const epoch = this.bridgeEpoch;
|
|
110
|
+
const result = await this.bridge.sendCommand('authenticateManaged', { config });
|
|
111
|
+
this.checkSession(result, epoch);
|
|
112
|
+
this.sessionGeneration = result.plan.generation;
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Create the planned exclusive /32 TUN and split routes with forwarding disabled.
|
|
117
|
+
* Caller must authorize these exact native transitions before calling. */
|
|
118
|
+
public prepareManagedTunnel(generation: string): Promise<IVpnManagedSession> {
|
|
119
|
+
return this.sessionCommand('prepareManagedTunnel', generation);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Reinspect, discard ready pre-enable inputs, then start forwarding. Call only
|
|
123
|
+
* after caller-owned policy is installed and verified against the prepared TUN. */
|
|
124
|
+
public enableManagedForwarding(generation: string): Promise<IVpnManagedSession> {
|
|
125
|
+
return this.sessionCommand('enableManagedForwarding', generation);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Terminally stop packet I/O and retain the TUN for caller-authorized teardown.
|
|
129
|
+
* This generation cannot forward again. Disconnect then closes its kernel owner. */
|
|
130
|
+
public stopManagedForwarding(generation: string): Promise<IVpnManagedSession> {
|
|
131
|
+
return this.sessionCommand('stopManagedForwarding', generation);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
public inspectManagedSession(generation: string): Promise<IVpnManagedSession> {
|
|
135
|
+
return this.sessionCommand('inspectManagedSession', generation);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private async sessionCommand(method: 'prepareManagedTunnel' | 'enableManagedForwarding'
|
|
139
|
+
| 'stopManagedForwarding' | 'inspectManagedSession', generation: string): Promise<IVpnManagedSession> {
|
|
140
|
+
if (typeof generation !== 'string' || !/^[a-f0-9]{32}$/.test(generation)) {
|
|
141
|
+
throw new Error('Managed session generation must be a native 128-bit identifier');
|
|
142
|
+
}
|
|
143
|
+
if (generation === this.invalidatedSessionGeneration || generation === this.retiredSessionGeneration) {
|
|
144
|
+
throw new Error('Managed session observation is unavailable');
|
|
145
|
+
}
|
|
146
|
+
const epoch = this.bridgeEpoch;
|
|
147
|
+
const result = await this.bridge.sendCommand(method, { generation });
|
|
148
|
+
this.checkSession(result, epoch, generation);
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private checkObservation(runtime: IVpnManagedRuntime, epoch: number, generation?: string): void {
|
|
153
|
+
if (epoch !== this.bridgeEpoch || runtime.generation === this.lastInvalidatedGeneration
|
|
154
|
+
|| (generation !== undefined && runtime.generation !== generation)) {
|
|
155
|
+
throw new Error('Managed runtime retired during the operation');
|
|
156
|
+
}
|
|
39
157
|
}
|
|
40
158
|
|
|
41
159
|
/**
|
|
@@ -48,12 +166,30 @@ export class VpnClient extends plugins.events.EventEmitter {
|
|
|
48
166
|
/**
|
|
49
167
|
* Connect to the VPN server using the provided config.
|
|
50
168
|
*/
|
|
51
|
-
public async connect(config?: IVpnClientConfig): Promise<
|
|
169
|
+
public async connect(config?: IVpnClientConfig): Promise<IVpnConnectionResult> {
|
|
52
170
|
const cfg = config || this.options.config;
|
|
53
171
|
if (!cfg) {
|
|
54
172
|
throw new Error('VpnClient.connect: no config provided');
|
|
55
173
|
}
|
|
56
|
-
|
|
174
|
+
const epoch = this.bridgeEpoch;
|
|
175
|
+
const result = await this.bridge.sendCommand('connect', { config: cfg });
|
|
176
|
+
if (result.managedRuntime) {
|
|
177
|
+
this.checkObservation(result.managedRuntime, epoch);
|
|
178
|
+
this.managedGeneration = result.managedRuntime.generation;
|
|
179
|
+
}
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Inspect the retained native TUN, its exact IPv4 /32 and split routes.
|
|
184
|
+
* Requires the connection's generation and fails after retirement/reconnect. */
|
|
185
|
+
public async inspectManagedRuntime(generation: string): Promise<IVpnManagedRuntime> {
|
|
186
|
+
if (typeof generation !== 'string' || !/^[a-f0-9]{32}$/.test(generation)) {
|
|
187
|
+
throw new Error('Managed runtime generation must be a native 128-bit identifier');
|
|
188
|
+
}
|
|
189
|
+
const epoch = this.bridgeEpoch;
|
|
190
|
+
const result = await this.bridge.sendCommand('inspectManagedRuntime', { generation });
|
|
191
|
+
this.checkObservation(result, epoch, generation);
|
|
192
|
+
return result;
|
|
57
193
|
}
|
|
58
194
|
|
|
59
195
|
/**
|
|
@@ -93,9 +229,11 @@ export class VpnClient extends plugins.events.EventEmitter {
|
|
|
93
229
|
}
|
|
94
230
|
|
|
95
231
|
/**
|
|
96
|
-
* Stop
|
|
232
|
+
* Stop and join an owned stdio daemon. Socket mode only closes the bridge;
|
|
233
|
+
* disconnect() must first join the independently owned native session.
|
|
97
234
|
*/
|
|
98
235
|
public async stop(): Promise<void> {
|
|
236
|
+
this.invalidateBridge('Client stop requested');
|
|
99
237
|
await this.bridge.stop();
|
|
100
238
|
}
|
|
101
239
|
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
export interface IVpnTransportStdio {
|
|
6
6
|
transport: 'stdio';
|
|
7
|
+
/** Exact caller-verified executable. Overrides environment/package lookup;
|
|
8
|
+
* invalid paths fail without falling through to another executable. */
|
|
9
|
+
binaryPath?: string;
|
|
7
10
|
}
|
|
8
11
|
|
|
9
12
|
export interface IVpnTransportSocket {
|
|
@@ -488,7 +491,13 @@ export interface IWgPeerInfo {
|
|
|
488
491
|
// ============================================================================
|
|
489
492
|
|
|
490
493
|
export type TVpnClientCommands = {
|
|
491
|
-
|
|
494
|
+
authenticateManaged: { params: { config: IVpnManagedSessionConfig }; result: IVpnManagedSession };
|
|
495
|
+
prepareManagedTunnel: { params: { generation: string }; result: IVpnManagedSession };
|
|
496
|
+
enableManagedForwarding: { params: { generation: string }; result: IVpnManagedSession };
|
|
497
|
+
stopManagedForwarding: { params: { generation: string }; result: IVpnManagedSession };
|
|
498
|
+
inspectManagedSession: { params: { generation: string }; result: IVpnManagedSession };
|
|
499
|
+
connect: { params: { config: IVpnClientConfig }; result: IVpnConnectionResult };
|
|
500
|
+
inspectManagedRuntime: { params: { generation: string }; result: IVpnManagedRuntime };
|
|
492
501
|
disconnect: { params: Record<string, never>; result: void };
|
|
493
502
|
getStatus: { params: Record<string, never>; result: IVpnStatus };
|
|
494
503
|
getStatistics: { params: Record<string, never>; result: IVpnStatistics };
|
|
@@ -595,6 +604,101 @@ export interface IManagedNetworkBinding {
|
|
|
595
604
|
nodeId: string;
|
|
596
605
|
}
|
|
597
606
|
|
|
607
|
+
export interface IVpnConnectionResult {
|
|
608
|
+
assignedIp: string;
|
|
609
|
+
/** Present only for an authenticated native managed connection with a real TUN. */
|
|
610
|
+
managedRuntime?: IVpnManagedRuntime;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/** Linux native client with explicit kernel-effect stages. The caller supplies
|
|
614
|
+
* a conservative deadline from its independently qualified clock authority. */
|
|
615
|
+
export interface IVpnManagedSessionConfig {
|
|
616
|
+
serverUrl: string;
|
|
617
|
+
serverPublicKey: string;
|
|
618
|
+
clientPrivateKey: string;
|
|
619
|
+
clientPublicKey: string;
|
|
620
|
+
transport: 'quic' | 'websocket';
|
|
621
|
+
serverCertHash?: string;
|
|
622
|
+
mtu?: number;
|
|
623
|
+
managedNetwork: IManagedNetworkBinding;
|
|
624
|
+
/** Same native boot; deadline must be future and at most fifteen minutes away.
|
|
625
|
+
* Expiry stops packet I/O even across suspend. It does not delete a held TUN. */
|
|
626
|
+
lease: { bootId: string; deadlineBoottimeMs: number };
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/** Authentication returns the full validated assignment and planned exclusive
|
|
630
|
+
* TUN name before creating a device or route. Kernel policy remains caller-owned. */
|
|
631
|
+
export interface IVpnManagedSessionPlan {
|
|
632
|
+
schemaVersion: 1;
|
|
633
|
+
generation: string;
|
|
634
|
+
namespace: { device: string; inode: string };
|
|
635
|
+
interfaceName: string;
|
|
636
|
+
assignment: {
|
|
637
|
+
assignedIp: string;
|
|
638
|
+
gateway: string;
|
|
639
|
+
subnet: string;
|
|
640
|
+
mtu: number;
|
|
641
|
+
effectiveMtu: number;
|
|
642
|
+
managed: IManagedNetworkAssignment;
|
|
643
|
+
};
|
|
644
|
+
mtu: number;
|
|
645
|
+
remoteIp: string;
|
|
646
|
+
lease: IVpnManagedSessionConfig['lease'];
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
export interface IVpnManagedSession {
|
|
650
|
+
plan: IVpnManagedSessionPlan;
|
|
651
|
+
phase: 'authenticated' | 'prepared' | 'forwarding' | 'stopped';
|
|
652
|
+
/** Fresh native inspection when present. Prepared TUN is UP with forwarding
|
|
653
|
+
* disabled. A stopped TUN stays owned until disconnect() joins deletion, or an
|
|
654
|
+
* owned stdio process is joined by stop(). Socket stop() does not delete it. */
|
|
655
|
+
runtime: IVpnManagedRuntime | null;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/** Stopped means the complete packet I/O future ended; retired means the native
|
|
659
|
+
* session owner ended. Neither proves namespace-wide cleanup, conntrack drain,
|
|
660
|
+
* packets already queued on the network, or safe allocation reuse. */
|
|
661
|
+
export interface IVpnManagedSessionEvent {
|
|
662
|
+
generation: string;
|
|
663
|
+
phase: 'stopped' | 'retired';
|
|
664
|
+
reason: string;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/** The facade lost observation of this generation. Native packet I/O and TUN
|
|
668
|
+
* ownership may remain alive, especially with an independent socket daemon.
|
|
669
|
+
* This is neither a stopped nor a retired native lifecycle acknowledgement. */
|
|
670
|
+
export interface IVpnManagedSessionInvalidation {
|
|
671
|
+
generation: string;
|
|
672
|
+
reason: string;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/** A fresh observation of a retained native lifetime, never a transferable or
|
|
676
|
+
* restorable capability. The caller retains VpnClient and listens for invalidation.
|
|
677
|
+
* IPv6, namespace-wide policy/rules, underlay and firewall remain caller-owned. */
|
|
678
|
+
export interface IVpnManagedRuntime {
|
|
679
|
+
schemaVersion: 1;
|
|
680
|
+
generation: string;
|
|
681
|
+
namespace: { device: string; inode: string };
|
|
682
|
+
link: {
|
|
683
|
+
interfaceIndex: number;
|
|
684
|
+
interfaceName: string;
|
|
685
|
+
interfaceKind: 'tun';
|
|
686
|
+
linkIndex: number;
|
|
687
|
+
mtu: number;
|
|
688
|
+
address: string;
|
|
689
|
+
prefixLength: 32;
|
|
690
|
+
};
|
|
691
|
+
remoteIp: string;
|
|
692
|
+
assignment: IManagedNetworkAssignment;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/** Stop using this generation immediately. This event is not a joined cleanup,
|
|
696
|
+
* conntrack drain, or allocation-reuse acknowledgement. */
|
|
697
|
+
export interface IVpnManagedRuntimeInvalidation {
|
|
698
|
+
generation: string;
|
|
699
|
+
reason: string;
|
|
700
|
+
}
|
|
701
|
+
|
|
598
702
|
export interface IManagedNodeConfig {
|
|
599
703
|
/** Caller-owned control address with /32; local workload prefixes are not hub routes. */
|
|
600
704
|
address: string;
|
|
@@ -632,6 +736,9 @@ export interface IVpnServiceUnit {
|
|
|
632
736
|
// ============================================================================
|
|
633
737
|
|
|
634
738
|
export interface IVpnEventMap {
|
|
739
|
+
'managed-session-state': IVpnManagedSessionEvent;
|
|
740
|
+
'managed-session-invalidated': IVpnManagedSessionInvalidation;
|
|
741
|
+
'managed-runtime-invalidated': IVpnManagedRuntimeInvalidation;
|
|
635
742
|
'status': IVpnStatus;
|
|
636
743
|
'error': { message: string; code?: string };
|
|
637
744
|
'client-connected': IVpnClientInfo;
|