@push.rocks/smartvpn 2.0.0 → 2.2.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.
@@ -1,6 +1,7 @@
1
1
  # Third-party notices: managed Linux routing
2
2
 
3
- The Linux daemon links the following unmodified registry dependencies. Their
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.0
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
 
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "format": "tsrust.build-provenance.v2",
3
- "binarySha256": "9dae4722a783f8de4ce2c5066eff1bf5d6ebde48750aaf3e96ad4e0374e153a3",
3
+ "binarySha256": "4f8072fe724ac6d140e4d76aaaa1820bbfae2d70eda8f5c93ffbe8717df56453",
4
4
  "buildInfo": {
5
5
  "projectName": "@push.rocks/smartvpn",
6
- "projectVersion": "2.0.0",
7
- "gitCommit": "d6dbad135253e924e10c2dd0183377cd64e0c347",
6
+ "projectVersion": "2.2.0",
7
+ "gitCommit": "da37d5bcd88b9b2d2849d05fa7865a361ab7c313",
8
8
  "gitDirty": false,
9
- "builtAt": "2026-09-07T08:10:39.362Z",
10
- "tsrustVersion": "1.11.0",
9
+ "builtAt": "2026-09-15T01:02:07.587Z",
10
+ "tsrustVersion": "1.13.0",
11
11
  "binary": "smartvpn_daemon",
12
12
  "target": "linux_amd64_musl"
13
13
  }
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "format": "tsrust.build-provenance.v2",
3
- "binarySha256": "939472012c3378f6e1727b32f7ee8d167a5566a918dd188c5e850190e0f10282",
3
+ "binarySha256": "01e551c5becdaaec42fa18c0f49ea6aa2e2bf6fef225211606ce52a486c1c918",
4
4
  "buildInfo": {
5
5
  "projectName": "@push.rocks/smartvpn",
6
- "projectVersion": "2.0.0",
7
- "gitCommit": "d6dbad135253e924e10c2dd0183377cd64e0c347",
6
+ "projectVersion": "2.2.0",
7
+ "gitCommit": "da37d5bcd88b9b2d2849d05fa7865a361ab7c313",
8
8
  "gitDirty": false,
9
- "builtAt": "2026-09-07T08:10:39.651Z",
10
- "tsrustVersion": "1.11.0",
9
+ "builtAt": "2026-09-15T01:02:07.887Z",
10
+ "tsrustVersion": "1.13.0",
11
11
  "binary": "smartvpn_daemon",
12
12
  "target": "linux_arm64_musl"
13
13
  }
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartvpn',
6
- version: '2.0.0',
6
+ version: '2.2.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=
@@ -13,6 +13,7 @@ export declare class VpnBridge<TCommands extends TCommandMap> extends plugins.ev
13
13
  transport: TVpnTransportOptions;
14
14
  mode: 'client' | 'server';
15
15
  binaryName?: string;
16
+ networkNamespaceFd?: number;
16
17
  });
17
18
  /**
18
19
  * Start the bridge: spawn in stdio mode or connect in socket mode.
@@ -7,20 +7,39 @@ import * as paths from './smartvpn.paths.js';
7
7
  export class VpnBridge extends plugins.events.EventEmitter {
8
8
  constructor(options) {
9
9
  super();
10
+ const namespaceFd = options.networkNamespaceFd;
11
+ if (namespaceFd !== undefined) {
12
+ if (plugins.os.platform() !== 'linux' || options.mode !== 'client' || options.transport.transport !== 'stdio') {
13
+ throw new Error('VpnBridge networkNamespaceFd requires a Linux stdio client');
14
+ }
15
+ if (!Number.isInteger(namespaceFd) || namespaceFd < 0 || namespaceFd > 0x7fff_ffff) {
16
+ throw new Error('VpnBridge networkNamespaceFd must be a non-negative native descriptor');
17
+ }
18
+ }
10
19
  const binaryName = options.binaryName || 'smartvpn_daemon';
11
- this.transportOptions = options.transport;
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
+ }
24
+ // Keep the validated process transport stable even if the caller edits its options.
25
+ this.transportOptions = { ...options.transport };
12
26
  this.mode = options.mode;
13
27
  this.bridge = new plugins.smartrust.RustBridge({
14
28
  binaryName,
15
29
  binaryPath: options.transport.transport === 'stdio'
16
- ? paths.getBinaryPath(binaryName)
30
+ ? options.transport.binaryPath ?? paths.getBinaryPath(binaryName)
17
31
  : undefined,
18
32
  localPaths: [],
19
33
  searchSystemPath: false,
20
- cliArgs: ['--management', '--mode', this.mode],
34
+ cliArgs: ['--management', '--mode', this.mode,
35
+ ...(namespaceFd === undefined ? [] : ['--network-namespace-fd', '3'])],
36
+ inheritedFileDescriptors: namespaceFd === undefined ? undefined : [namespaceFd],
21
37
  maxPayloadSize: 10 * 1024 * 1024, // 10 MB
22
38
  });
23
39
  // Forward events from inner bridge
40
+ this.bridge.on('disconnected', (event) => {
41
+ this.emit('disconnected', event);
42
+ });
24
43
  this.bridge.on('exit', (code, signal) => {
25
44
  this.emit('exit', code, signal);
26
45
  });
@@ -32,6 +51,9 @@ export class VpnBridge extends plugins.events.EventEmitter {
32
51
  });
33
52
  // Forward management events from the daemon
34
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
+ });
35
57
  this.bridge.on('management:status', (data) => {
36
58
  this.emit('status', data);
37
59
  });
@@ -88,4 +110,4 @@ export class VpnBridge extends plugins.events.EventEmitter {
88
110
  return this.bridge.running;
89
111
  }
90
112
  }
91
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnR2cG4uY2xhc3Nlcy52cG5icmlkZ2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydHZwbi5jbGFzc2VzLnZwbmJyaWRnZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHVCQUF1QixDQUFDO0FBQ2pELE9BQU8sS0FBSyxLQUFLLE1BQU0scUJBQXFCLENBQUM7QUFPN0M7OztHQUdHO0FBQ0gsTUFBTSxPQUFPLFNBQXlDLFNBQVEsT0FBTyxDQUFDLE1BQU0sQ0FBQyxZQUFZO0lBS3ZGLFlBQVksT0FJWDtRQUNDLEtBQUssRUFBRSxDQUFDO1FBRVIsTUFBTSxVQUFVLEdBQUcsT0FBTyxDQUFDLFVBQVUsSUFBSSxpQkFBaUIsQ0FBQztRQUMzRCxJQUFJLENBQUMsZ0JBQWdCLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBQztRQUMxQyxJQUFJLENBQUMsSUFBSSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUM7UUFFekIsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLE9BQU8sQ0FBQyxTQUFTLENBQUMsVUFBVSxDQUFZO1lBQ3hELFVBQVU7WUFDVixVQUFVLEVBQUUsT0FBTyxDQUFDLFNBQVMsQ0FBQyxTQUFTLEtBQUssT0FBTztnQkFDakQsQ0FBQyxDQUFDLEtBQUssQ0FBQyxhQUFhLENBQUMsVUFBVSxDQUFDO2dCQUNqQyxDQUFDLENBQUMsU0FBUztZQUNiLFVBQVUsRUFBRSxFQUFFO1lBQ2QsZ0JBQWdCLEVBQUUsS0FBSztZQUN2QixPQUFPLEVBQUUsQ0FBQyxjQUFjLEVBQUUsUUFBUSxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUM7WUFDOUMsY0FBYyxFQUFFLEVBQUUsR0FBRyxJQUFJLEdBQUcsSUFBSSxFQUFFLFFBQVE7U0FDM0MsQ0FBQyxDQUFDO1FBRUgsbUNBQW1DO1FBQ25DLElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxDQUFDLElBQW1CLEVBQUUsTUFBcUIsRUFBRSxFQUFFO1lBQ3BFLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLElBQUksRUFBRSxNQUFNLENBQUMsQ0FBQztRQUNsQyxDQUFDLENBQUMsQ0FBQztRQUNILElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLGFBQWEsRUFBRSxHQUFHLEVBQUU7WUFDakMsSUFBSSxDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsQ0FBQztRQUMzQixDQUFDLENBQUMsQ0FBQztRQUNILElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLFFBQVEsRUFBRSxDQUFDLElBQVksRUFBRSxFQUFFO1lBQ3hDLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxDQUFDO1FBQzVCLENBQUMsQ0FBQyxDQUFDO1FBRUgsNENBQTRDO1FBQzVDLGtFQUFrRTtRQUNsRSxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxtQkFBbUIsRUFBRSxDQUFDLElBQVMsRUFBRSxFQUFFO1lBQ2hELElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxDQUFDO1FBQzVCLENBQUMsQ0FBQyxDQUFDO1FBQ0gsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsa0JBQWtCLEVBQUUsQ0FBQyxJQUFTLEVBQUUsRUFBRTtZQUMvQyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsQ0FBQztRQUMzQixDQUFDLENBQUMsQ0FBQztRQUNILElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLDZCQUE2QixFQUFFLENBQUMsSUFBUyxFQUFFLEVBQUU7WUFDMUQsSUFBSSxDQUFDLElBQUksQ0FBQyxrQkFBa0IsRUFBRSxJQUFJLENBQUMsQ0FBQztRQUN0QyxDQUFDLENBQUMsQ0FBQztRQUNILElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLGdDQUFnQyxFQUFFLENBQUMsSUFBUyxFQUFFLEVBQUU7WUFDN0QsSUFBSSxDQUFDLElBQUksQ0FBQyxxQkFBcUIsRUFBRSxJQUFJLENBQUMsQ0FBQztRQUN6QyxDQUFDLENBQUMsQ0FBQztRQUNILElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLG9CQUFvQixFQUFFLENBQUMsSUFBUyxFQUFFLEVBQUU7WUFDakQsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDN0IsQ0FBQyxDQUFDLENBQUM7UUFDSCxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxvQkFBb0IsRUFBRSxDQUFDLElBQVMsRUFBRSxFQUFFO1lBQ2pELElBQUksQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLElBQUksQ0FBQyxDQUFDO1FBQzdCLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLEtBQUs7UUFDaEIsSUFBSSxJQUFJLENBQUMsZ0JBQWdCLENBQUMsU0FBUyxLQUFLLFFBQVEsRUFBRSxDQUFDO1lBQ2pELE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxnQkFBdUMsQ0FBQztZQUNoRSxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxVQUFVLEVBQUU7Z0JBQ2hELGFBQWEsRUFBRSxVQUFVLENBQUMsYUFBYTtnQkFDdkMsb0JBQW9CLEVBQUUsVUFBVSxDQUFDLG9CQUFvQjtnQkFDckQsbUJBQW1CLEVBQUUsVUFBVSxDQUFDLG1CQUFtQjtnQkFDbkQsb0JBQW9CLEVBQUUsVUFBVSxDQUFDLG9CQUFvQjthQUN0RCxDQUFDLENBQUM7UUFDTCxDQUFDO2FBQU0sQ0FBQztZQUNOLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUM3QixDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7T0FHRztJQUNJLEtBQUssQ0FBQyxJQUFJO1FBQ2YsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLFNBQVMsRUFBRSxDQUFDO0lBQ2hDLENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxXQUFXLENBQ3RCLE1BQVMsRUFDVCxNQUE4QjtRQUU5QixPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLE1BQU0sRUFBRSxNQUFNLENBQUMsQ0FBQztJQUNqRCxDQUFDO0lBRUQ7O09BRUc7SUFDSCxJQUFXLE9BQU87UUFDaEIsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQztJQUM3QixDQUFDO0NBQ0YifQ==
113
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnR2cG4uY2xhc3Nlcy52cG5icmlkZ2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydHZwbi5jbGFzc2VzLnZwbmJyaWRnZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHVCQUF1QixDQUFDO0FBQ2pELE9BQU8sS0FBSyxLQUFLLE1BQU0scUJBQXFCLENBQUM7QUFRN0M7OztHQUdHO0FBQ0gsTUFBTSxPQUFPLFNBQXlDLFNBQVEsT0FBTyxDQUFDLE1BQU0sQ0FBQyxZQUFZO0lBS3ZGLFlBQVksT0FLWDtRQUNDLEtBQUssRUFBRSxDQUFDO1FBRVIsTUFBTSxXQUFXLEdBQUcsT0FBTyxDQUFDLGtCQUFrQixDQUFDO1FBQy9DLElBQUksV0FBVyxLQUFLLFNBQVMsRUFBRSxDQUFDO1lBQzlCLElBQUksT0FBTyxDQUFDLEVBQUUsQ0FBQyxRQUFRLEVBQUUsS0FBSyxPQUFPLElBQUksT0FBTyxDQUFDLElBQUksS0FBSyxRQUFRLElBQUksT0FBTyxDQUFDLFNBQVMsQ0FBQyxTQUFTLEtBQUssT0FBTyxFQUFFLENBQUM7Z0JBQzlHLE1BQU0sSUFBSSxLQUFLLENBQUMsNERBQTRELENBQUMsQ0FBQztZQUNoRixDQUFDO1lBQ0QsSUFBSSxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsV0FBVyxDQUFDLElBQUksV0FBVyxHQUFHLENBQUMsSUFBSSxXQUFXLEdBQUcsV0FBVyxFQUFFLENBQUM7Z0JBQ25GLE1BQU0sSUFBSSxLQUFLLENBQUMsdUVBQXVFLENBQUMsQ0FBQztZQUMzRixDQUFDO1FBQ0gsQ0FBQztRQUNELE1BQU0sVUFBVSxHQUFHLE9BQU8sQ0FBQyxVQUFVLElBQUksaUJBQWlCLENBQUM7UUFDM0QsSUFBSSxPQUFPLENBQUMsU0FBUyxDQUFDLFNBQVMsS0FBSyxPQUFPLElBQUksT0FBTyxDQUFDLFNBQVMsQ0FBQyxVQUFVLEtBQUssU0FBUztlQUNwRixDQUFDLE9BQU8sT0FBTyxD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
@@ -1,12 +1,18 @@
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 } 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?;
9
12
  constructor(options: IVpnClientOptions);
13
+ private invalidateGeneration;
14
+ private invalidateBridge;
15
+ private checkObservation;
10
16
  /**
11
17
  * Start the daemon bridge (spawn or connect).
12
18
  */
@@ -14,9 +20,10 @@ export declare class VpnClient extends plugins.events.EventEmitter {
14
20
  /**
15
21
  * Connect to the VPN server using the provided config.
16
22
  */
17
- connect(config?: IVpnClientConfig): Promise<{
18
- assignedIp: string;
19
- }>;
23
+ connect(config?: IVpnClientConfig): Promise<IVpnConnectionResult>;
24
+ /** Inspect the retained native TUN, its exact IPv4 /32 and split routes.
25
+ * Requires the connection's generation and fails after retirement/reconnect. */
26
+ inspectManagedRuntime(generation: string): Promise<IVpnManagedRuntime>;
20
27
  /**
21
28
  * Disconnect from the VPN server.
22
29
  */
@@ -6,21 +6,50 @@ 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,
12
13
  mode: 'client',
14
+ networkNamespaceFd: options.networkNamespaceFd,
13
15
  });
14
16
  // Forward bridge events
17
+ this.bridge.on('disconnected', () => {
18
+ this.invalidateBridge('Client transport disconnected');
19
+ });
15
20
  this.bridge.on('exit', (code, signal) => {
21
+ this.invalidateBridge('Client bridge exited');
16
22
  this.emit('exit', { code, signal });
17
23
  });
18
24
  this.bridge.on('reconnected', () => {
25
+ this.invalidateBridge('Client bridge reconnected');
19
26
  this.emit('reconnected');
20
27
  });
21
28
  this.bridge.on('stderr', (line) => {
22
29
  this.emit('stderr', line);
23
30
  });
31
+ this.bridge.on('managed-runtime-invalidated', (event) => {
32
+ this.invalidateGeneration(event);
33
+ });
34
+ }
35
+ invalidateGeneration(event) {
36
+ if (this.lastInvalidatedGeneration === event.generation)
37
+ return;
38
+ this.lastInvalidatedGeneration = event.generation;
39
+ if (this.managedGeneration === event.generation)
40
+ this.managedGeneration = undefined;
41
+ this.emit('managed-runtime-invalidated', event);
42
+ }
43
+ invalidateBridge(reason) {
44
+ this.bridgeEpoch++;
45
+ if (this.managedGeneration)
46
+ this.invalidateGeneration({ generation: this.managedGeneration, reason });
47
+ }
48
+ checkObservation(runtime, epoch, generation) {
49
+ if (epoch !== this.bridgeEpoch || runtime.generation === this.lastInvalidatedGeneration
50
+ || (generation !== undefined && runtime.generation !== generation)) {
51
+ throw new Error('Managed runtime retired during the operation');
52
+ }
24
53
  }
25
54
  /**
26
55
  * Start the daemon bridge (spawn or connect).
@@ -36,7 +65,24 @@ export class VpnClient extends plugins.events.EventEmitter {
36
65
  if (!cfg) {
37
66
  throw new Error('VpnClient.connect: no config provided');
38
67
  }
39
- return this.bridge.sendCommand('connect', { config: cfg });
68
+ const epoch = this.bridgeEpoch;
69
+ const result = await this.bridge.sendCommand('connect', { config: cfg });
70
+ if (result.managedRuntime) {
71
+ this.checkObservation(result.managedRuntime, epoch);
72
+ this.managedGeneration = result.managedRuntime.generation;
73
+ }
74
+ return result;
75
+ }
76
+ /** Inspect the retained native TUN, its exact IPv4 /32 and split routes.
77
+ * Requires the connection's generation and fails after retirement/reconnect. */
78
+ async inspectManagedRuntime(generation) {
79
+ if (typeof generation !== 'string' || !/^[a-f0-9]{32}$/.test(generation)) {
80
+ throw new Error('Managed runtime generation must be a native 128-bit identifier');
81
+ }
82
+ const epoch = this.bridgeEpoch;
83
+ const result = await this.bridge.sendCommand('inspectManagedRuntime', { generation });
84
+ this.checkObservation(result, epoch, generation);
85
+ return result;
40
86
  }
41
87
  /**
42
88
  * Disconnect from the VPN server.
@@ -73,6 +119,7 @@ export class VpnClient extends plugins.events.EventEmitter {
73
119
  * Stop the daemon bridge and wait for confirmed ownership release.
74
120
  */
75
121
  async stop() {
122
+ this.invalidateBridge('Client stop requested');
76
123
  await this.bridge.stop();
77
124
  }
78
125
  /**
@@ -82,4 +129,4 @@ export class VpnClient extends plugins.events.EventEmitter {
82
129
  return this.bridge.running;
83
130
  }
84
131
  }
85
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnR2cG4uY2xhc3Nlcy52cG5jbGllbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydHZwbi5jbGFzc2VzLnZwbmNsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHVCQUF1QixDQUFDO0FBQ2pELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQVc1RDs7R0FFRztBQUNILE1BQU0sT0FBTyxTQUFVLFNBQVEsT0FBTyxDQUFDLE1BQU0sQ0FBQyxZQUFZO0lBSXhELFlBQVksT0FBMEI7UUFDcEMsS0FBSyxFQUFFLENBQUM7UUFDUixJQUFJLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQztRQUN2QixJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksU0FBUyxDQUFxQjtZQUM5QyxTQUFTLEVBQUUsT0FBTyxDQUFDLFNBQVM7WUFDNUIsSUFBSSxFQUFFLFFBQVE7U0FDZixDQUFDLENBQUM7UUFFSCx3QkFBd0I7UUFDeEIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLENBQUMsSUFBbUIsRUFBRSxNQUFxQixFQUFFLEVBQUU7WUFDcEUsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFLENBQUMsQ0FBQztRQUN0QyxDQUFDLENBQUMsQ0FBQztRQUNILElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLGFBQWEsRUFBRSxHQUFHLEVBQUU7WUFDakMsSUFBSSxDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsQ0FBQztRQUMzQixDQUFDLENBQUMsQ0FBQztRQUNILElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLFFBQVEsRUFBRSxDQUFDLElBQVksRUFBRSxFQUFFO1lBQ3hDLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxDQUFDO1FBQzVCLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLEtBQUs7UUFDaEIsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQzdCLENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxPQUFPLENBQUMsTUFBeUI7UUFDNUMsTUFBTSxHQUFHLEdBQUcsTUFBTSxJQUFJLElBQUksQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDO1FBQzFDLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztZQUNULE1BQU0sSUFBSSxLQUFLLENBQUMsdUNBQXVDLENBQUMsQ0FBQztRQUMzRCxDQUFDO1FBQ0QsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQyxTQUFTLEVBQUUsRUFBRSxNQUFNLEVBQUUsR0FBRyxFQUFFLENBQUMsQ0FBQztJQUM3RCxDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLLENBQUMsVUFBVTtRQUNyQixNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLFlBQVksRUFBRSxFQUEyQixDQUFDLENBQUM7SUFDM0UsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLFNBQVM7UUFDcEIsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQyxXQUFXLEVBQUUsRUFBMkIsQ0FBQyxDQUFDO0lBQzNFLENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxhQUFhO1FBQ3hCLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsZUFBZSxFQUFFLEVBQTJCLENBQUMsQ0FBQztJQUMvRSxDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLLENBQUMsb0JBQW9CO1FBQy9CLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsc0JBQXNCLEVBQUUsRUFBMkIsQ0FBQyxDQUFDO0lBQ3RGLENBQUM7SUFFRDs7O09BR0c7SUFDSSxLQUFLLENBQUMsVUFBVTtRQUNyQixPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLFlBQVksRUFBRSxFQUEyQixDQUFDLENBQUM7SUFDNUUsQ0FBQztJQUVEOztPQUVHO0lBQ0ksS0FBSyxDQUFDLElBQUk7UUFDZixNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDM0IsQ0FBQztJQUVEOztPQUVHO0lBQ0gsSUFBVyxPQUFPO1FBQ2hCLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUM7SUFDN0IsQ0FBQztDQUNGIn0=
132
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnR2cG4uY2xhc3Nlcy52cG5jbGllbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9zbWFydHZwbi5jbGFzc2VzLnZwbmNsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHVCQUF1QixDQUFDO0FBQ2pELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQWM1RDs7R0FFRztBQUNILE1BQU0sT0FBTyxTQUFVLFNBQVEsT0FBTyxDQUFDLE1BQU0sQ0FBQyxZQUFZO0lBT3hELFlBQVksT0FBMEI7UUFDcEMsS0FBSyxFQUFFLENBQUM7UUFMRixnQkFBVyxHQUFHLENBQUMsQ0FBQztRQU10QixJQUFJLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQztRQUN2QixJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksU0FBUyxDQUFxQjtZQUM5QyxTQUFTLEVBQUUsT0FBTyxDQUFDLFNBQVM7WUFDNUIsSUFBSSxFQUFFLFFBQVE7WUFDZCxrQkFBa0IsRUFBRSxPQUFPLENBQUMsa0JBQWtCO1NBQy9DLENBQUMsQ0FBQztRQUVILHdCQUF3QjtRQUN4QixJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxjQUFjLEVBQUUsR0FBRyxFQUFFO1lBQ2xDLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQywrQkFBK0IsQ0FBQyxDQUFDO1FBQ3pELENBQUMsQ0FBQyxDQUFDO1FBQ0gsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLENBQUMsSUFBbUIsRUFBRSxNQUFxQixFQUFFLEVBQUU7WUFDcEUsSUFBSSxDQUFDLGdCQUFnQixDQUFDLHNCQUFzQixDQUFDLENBQUM7WUFDOUMsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsRUFBRSxJQUFJLEVBQUUsTUFBTSxFQUFFLENBQUMsQ0FBQztRQUN0QyxDQUFDLENBQUMsQ0FBQztRQUNILElBQUksQ0FBQyxNQUFNLENBQUMs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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';
@@ -73,6 +76,10 @@ export type IVpnClientConfig = TVpnClientConfig;
73
76
  export interface IVpnClientOptions {
74
77
  transport: TVpnTransportOptions;
75
78
  config?: IVpnClientConfig;
79
+ /** Linux stdio only. Caller-owned network namespace FD, retained through every spawn.
80
+ * The complete native client enters before threads, sockets, TUN or readiness.
81
+ * The caller owns namespace creation, underlay routing, DNS and packet policy. */
82
+ networkNamespaceFd?: number;
76
83
  }
77
84
  export interface IVpnServerConfig {
78
85
  /** Listen address for WebSocket, e.g. 0.0.0.0:443 */
@@ -404,9 +411,13 @@ export type TVpnClientCommands = {
404
411
  params: {
405
412
  config: IVpnClientConfig;
406
413
  };
407
- result: {
408
- assignedIp: string;
414
+ result: IVpnConnectionResult;
415
+ };
416
+ inspectManagedRuntime: {
417
+ params: {
418
+ generation: string;
409
419
  };
420
+ result: IVpnManagedRuntime;
410
421
  };
411
422
  disconnect: {
412
423
  params: Record<string, never>;
@@ -645,6 +656,39 @@ export interface IManagedNetworkBinding {
645
656
  authorityId: string;
646
657
  nodeId: string;
647
658
  }
659
+ export interface IVpnConnectionResult {
660
+ assignedIp: string;
661
+ /** Present only for an authenticated native managed connection with a real TUN. */
662
+ managedRuntime?: IVpnManagedRuntime;
663
+ }
664
+ /** A fresh observation of a retained native lifetime, never a transferable or
665
+ * restorable capability. The caller retains VpnClient and listens for invalidation.
666
+ * IPv6, namespace-wide policy/rules, underlay and firewall remain caller-owned. */
667
+ export interface IVpnManagedRuntime {
668
+ schemaVersion: 1;
669
+ generation: string;
670
+ namespace: {
671
+ device: string;
672
+ inode: string;
673
+ };
674
+ link: {
675
+ interfaceIndex: number;
676
+ interfaceName: string;
677
+ interfaceKind: 'tun';
678
+ linkIndex: number;
679
+ mtu: number;
680
+ address: string;
681
+ prefixLength: 32;
682
+ };
683
+ remoteIp: string;
684
+ assignment: IManagedNetworkAssignment;
685
+ }
686
+ /** Stop using this generation immediately. This event is not a joined cleanup,
687
+ * conntrack drain, or allocation-reuse acknowledgement. */
688
+ export interface IVpnManagedRuntimeInvalidation {
689
+ generation: string;
690
+ reason: string;
691
+ }
648
692
  export interface IManagedNodeConfig {
649
693
  /** Caller-owned control address with /32; local workload prefixes are not hub routes. */
650
694
  address: string;
@@ -670,6 +714,7 @@ export interface IVpnServiceUnit {
670
714
  installPath: string;
671
715
  }
672
716
  export interface IVpnEventMap {
717
+ 'managed-runtime-invalidated': IVpnManagedRuntimeInvalidation;
673
718
  'status': IVpnStatus;
674
719
  'error': {
675
720
  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 &quot;AS IS&quot;, 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 &quot;Software&quot;), 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 &quot;AS IS&quot;, 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>
@@ -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": "da4de43c4e7a7e530af740c58c45ea620f9da2bf1639251d1740344102317aed",
6
+ "assets/third-party-licenses.md": "6be2373e39312d90eb3571bef614c89df565f98c917795d7aea3497f52ef19f1",
7
7
  "notices/aho-corasick-license.txt": "0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f",
8
- "notices/cargo-dependencies.html": "01f390d02b89be9fde6154ee05f3cb275e7baaf4ba15a29a3b4a385e4574a3ad",
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": "5e67e43571bdceaa39aa92783c485f3edd00cca975f3ffe44c010c527f6c9b38",
20
- "rust/Cargo.toml": "419e4aeb2dd5b28ede23310d7d9fb2b0308c37393154578999859951b6c947ca",
19
+ "rust/Cargo.lock": "93980ffcc3d2eca0083f524999b15f1674698d2aceff5922ed0aa5a09138d260",
20
+ "rust/Cargo.toml": "308d11b4bb6b8f290ca2d83f2c817e10798bad54510dd3a6fdb72c32d095ae3b",
21
21
  "rust/about.hbs": "a7c35d089563a08520a6e5f351aadae659e1f7bf24cabedbca3de8a89e966f86",
22
- "rust/about.toml": "9eb1fb41fc435a370d0c2ce1a77e5f0897b785a023ab87fd6721979ded479e66",
23
- "third-party-notices.md": "e935f4b27acaa567180b4b55ab472412b3c0ab7d528351b41863a1785074cd4b"
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.0",
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.0.0",
3
+ "version": "2.2.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.0.0"
28
+ "@push.rocks/smartrust": "^2.1.0"
29
29
  },
30
30
  "devDependencies": {
31
- "@git.zone/tsbuild": "^4.4.2",
31
+ "@git.zone/tsbuild": "^4.4.3",
32
32
  "@git.zone/tsrun": "^2.0.6",
33
- "@git.zone/tsrust": "^1.11.0",
34
- "@git.zone/tstest": "^4.0.0",
35
- "@types/node": "^26.4.1"
33
+ "@git.zone/tsrust": "^1.13.0",
34
+ "@git.zone/tstest": "^6.0.0",
35
+ "@types/node": "^26.5.0"
36
36
  },
37
37
  "files": [
38
38
  "ts/**/*",
@@ -47,6 +47,12 @@
47
47
  "browserslist": [
48
48
  "last 1 chrome versions"
49
49
  ],
50
+ "publishConfig": {
51
+ "executableFiles": [
52
+ "./dist_rust/smartvpn_daemon_linux_amd64_musl",
53
+ "./dist_rust/smartvpn_daemon_linux_arm64_musl"
54
+ ]
55
+ },
50
56
  "scripts": {
51
57
  "build": "(tsbuild tsfolders) && (tsrust)",
52
58
  "test:before": "(tsrust)",
package/readme.md CHANGED
@@ -124,6 +124,83 @@ console.log(`Connected! VPN IP: ${assignedIp}`);
124
124
 
125
125
  ## Features ✨
126
126
 
127
+ ### Caller-owned client network namespace
128
+
129
+ A Linux stdio client accepts `networkNamespaceFd` in `IVpnClientOptions`. Pass an
130
+ already open network namespace descriptor; the caller retains ownership and keeps
131
+ it valid through each `start()`. The child validates its inherited duplicate,
132
+ enters the namespace before creating runtime threads or sockets, then closes the
133
+ duplicate. Invalid descriptors and failed entry cannot produce a ready client.
134
+ Stopping the client waits for process termination before the caller releases its
135
+ namespace owner. A running child remains in its namespace if the caller closes
136
+ the original descriptor, but a later start still requires a valid descriptor.
137
+
138
+ The caller must provide the hub endpoint's underlay route and any required DNS
139
+ reachability inside that namespace before connecting. SmartVPN creates neither
140
+ the namespace nor that underlay. Managed TUN setup still validates the authenticated
141
+ assignment and rejects routes that capture the actual hub endpoint. Namespace
142
+ placement does not grant workload access or replace packet policy.
143
+
144
+ The option is process configuration, separate from VPN connection configuration.
145
+ It is rejected for socket transport and server bridges. The native equivalent is
146
+ `--management --mode client --network-namespace-fd 3`, with an inherited descriptor
147
+ at FD 3. Server facade firewall setup runs in its parent process, so server
148
+ namespace placement requires a separately coordinated policy owner.
149
+
150
+ The Linux amd64 musl candidate passed offline Linux 6.18.35 qualification with
151
+ all native threads in the target namespace, child descriptor closure, retained
152
+ caller ownership and denied-entry rejection. The shared `serve.zone/testing`
153
+ scenario also passed real WS/Noise traffic through inherited namespace entry,
154
+ managed TUN creation, allow/deny, reconnect, revocation, route-conflict rollback
155
+ and child-termination cleanup. Privileged ARM64 and Pallet integration remain
156
+ separate qualification requirements.
157
+
158
+ ### Retained managed TUN runtime
159
+
160
+ An authenticated native managed connection with `forwardingMode: 'tun'` returns
161
+ `managedRuntime` alongside `assignedIp`. Its random `generation` identifies that
162
+ one connection's retained TUN and netlink owner. The observation includes the
163
+ actual namespace device/inode, TUN name/index/link index, MTU, IPv4 /32, actual hub
164
+ peer IP and authenticated managed assignment. Testing and WireGuard connections
165
+ do not provide this capability.
166
+
167
+ `await client.inspectManagedRuntime(generation)` performs a new bounded kernel
168
+ inspection of that retained owner. It requires complete, uninterrupted link,
169
+ IPv4 address and route inventories, including successful netlink DONE messages.
170
+ A missing, retired or different generation rejects; no serialized observation
171
+ can reopen a namespace, adopt another interface or restore an old connection.
172
+
173
+ Register `client.on('managed-runtime-invalidated', handler)` before connecting.
174
+ Stop admitting work against that generation as soon as the event arrives or an
175
+ inspection/IPC operation fails. The native owner subscribes before its first
176
+ inspection and retires on any observed change to the owned link or IPv4 address,
177
+ its routes, or an equal/more-specific IPv4 route to its managed destinations.
178
+ A change followed by restoration still retires the generation. Protocol closure,
179
+ malformed inventory, interruption and overflow fail closed. Reconnecting creates
180
+ a new generation. This event is also emitted immediately when the facade loses
181
+ its transport, including the gap before automatic socket reconnect, or starts
182
+ stopping. Reattaching to a surviving daemon cannot revive an invalidated generation;
183
+ establish a fresh managed connection. The event does not acknowledge joined cleanup
184
+ or reusable allocations.
185
+
186
+ The native monitor shares the joined packet-task lifetime. On retirement the
187
+ packet future and its nonpersistent TUN close before the native event is emitted;
188
+ the kernel removes the TUN's routes. There is no name-based route cleanup or
189
+ detached monitor. `disconnect()` joins the native connection; `stop()` confirms
190
+ the owned bridge's closure. Socket transport still leaves the separately owned
191
+ daemon process alive, so use `disconnect()` for native connection teardown.
192
+
193
+ These are IPv4 TUN facts, not namespace-wide routing or packet-policy authority.
194
+ The caller owns underlay/DHCP changes, global routing rules, sysctls, IPv6 policy,
195
+ firewall enforcement and workload admission. Normal `getStatus()` is an observed
196
+ connection status and does not substitute for a fresh kernel inspection.
197
+
198
+ For sealed distributions, pass an already verified executable as
199
+ `transport: { transport: 'stdio', binaryPath: '/absolute/path/to/smartvpn_daemon' }`.
200
+ This selects that exact file ahead of environment/package lookup. Invalid paths
201
+ reject without another executable being selected. Namespace descriptors remain
202
+ separate `IVpnClientOptions.networkNamespaceFd` configuration.
203
+
127
204
  ### Confirmed bridge shutdown
128
205
 
129
206
  Always await `client.stop()`, `server.stop()` or `bridge.stop()` before replacing
@@ -145,8 +222,8 @@ await server.stop();
145
222
  ```
146
223
 
147
224
  This bridge lifecycle guarantee is separate from per-client packet/session
148
- revocation inside the running Rust daemon; see `readme.hints.md` for the current
149
- managed-network qualification boundaries.
225
+ revocation inside the running Rust daemon; see the managed-network qualification
226
+ boundaries below.
150
227
 
151
228
  The Rust server now waits for configured listeners and initial WireGuard peers
152
229
  before acknowledging startup. A listener startup failure shuts down the owned
@@ -774,6 +851,7 @@ client.on('exit', ({ code, signal }) => { /* daemon process exited */ });
774
851
  | `exit` | Both | `{ code, signal }` — daemon process exited |
775
852
  | `reconnected` | Both | `void` — socket transport reconnected |
776
853
  | `stderr` | Both | `string` — daemon diagnostic line, forwarded without automatic logging |
854
+ | `managed-runtime-invalidated` | Client | `IVpnManagedRuntimeInvalidation` — stop using this managed generation immediately |
777
855
 
778
856
  For connection state and telemetry, use `getStatus()`, `getStatistics()`, `listClients()`, and `getClientTelemetry()`.
779
857