@push.rocks/smartvpn 1.0.3

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.
Files changed (35) hide show
  1. package/dist_rust/smartvpn_daemon_linux_amd64 +0 -0
  2. package/dist_rust/smartvpn_daemon_linux_arm64 +0 -0
  3. package/dist_ts/00_commitinfo_data.d.ts +8 -0
  4. package/dist_ts/00_commitinfo_data.js +9 -0
  5. package/dist_ts/index.d.ts +6 -0
  6. package/dist_ts/index.js +7 -0
  7. package/dist_ts/smartvpn.classes.vpnbridge.d.ts +34 -0
  8. package/dist_ts/smartvpn.classes.vpnbridge.js +123 -0
  9. package/dist_ts/smartvpn.classes.vpnclient.d.ts +40 -0
  10. package/dist_ts/smartvpn.classes.vpnclient.js +69 -0
  11. package/dist_ts/smartvpn.classes.vpnconfig.d.ts +30 -0
  12. package/dist_ts/smartvpn.classes.vpnconfig.js +98 -0
  13. package/dist_ts/smartvpn.classes.vpninstaller.d.ts +37 -0
  14. package/dist_ts/smartvpn.classes.vpninstaller.js +105 -0
  15. package/dist_ts/smartvpn.classes.vpnserver.d.ts +46 -0
  16. package/dist_ts/smartvpn.classes.vpnserver.js +85 -0
  17. package/dist_ts/smartvpn.interfaces.d.ts +167 -0
  18. package/dist_ts/smartvpn.interfaces.js +5 -0
  19. package/dist_ts/smartvpn.paths.d.ts +1 -0
  20. package/dist_ts/smartvpn.paths.js +3 -0
  21. package/dist_ts/smartvpn.plugins.d.ts +9 -0
  22. package/dist_ts/smartvpn.plugins.js +12 -0
  23. package/license.md +21 -0
  24. package/package.json +52 -0
  25. package/readme.md +410 -0
  26. package/ts/00_commitinfo_data.ts +8 -0
  27. package/ts/index.ts +6 -0
  28. package/ts/smartvpn.classes.vpnbridge.ts +152 -0
  29. package/ts/smartvpn.classes.vpnclient.ts +87 -0
  30. package/ts/smartvpn.classes.vpnconfig.ts +104 -0
  31. package/ts/smartvpn.classes.vpninstaller.ts +126 -0
  32. package/ts/smartvpn.classes.vpnserver.ts +107 -0
  33. package/ts/smartvpn.interfaces.ts +166 -0
  34. package/ts/smartvpn.paths.ts +6 -0
  35. package/ts/smartvpn.plugins.ts +14 -0
@@ -0,0 +1,104 @@
1
+ import * as plugins from './smartvpn.plugins.js';
2
+ import type {
3
+ IVpnClientConfig,
4
+ IVpnServerConfig,
5
+ } from './smartvpn.interfaces.js';
6
+
7
+ /**
8
+ * VPN configuration loader, saver, and validator.
9
+ */
10
+ export class VpnConfig {
11
+ /**
12
+ * Validate a client config object. Throws on invalid config.
13
+ */
14
+ public static validateClientConfig(config: IVpnClientConfig): void {
15
+ if (!config.serverUrl) {
16
+ throw new Error('VpnConfig: serverUrl is required');
17
+ }
18
+ if (!config.serverUrl.startsWith('wss://') && !config.serverUrl.startsWith('ws://')) {
19
+ throw new Error('VpnConfig: serverUrl must start with wss:// or ws://');
20
+ }
21
+ if (!config.serverPublicKey) {
22
+ throw new Error('VpnConfig: serverPublicKey is required');
23
+ }
24
+ if (config.mtu !== undefined && (config.mtu < 576 || config.mtu > 65535)) {
25
+ throw new Error('VpnConfig: mtu must be between 576 and 65535');
26
+ }
27
+ if (config.keepaliveIntervalSecs !== undefined && config.keepaliveIntervalSecs < 1) {
28
+ throw new Error('VpnConfig: keepaliveIntervalSecs must be >= 1');
29
+ }
30
+ if (config.dns) {
31
+ for (const dns of config.dns) {
32
+ if (!VpnConfig.isValidIp(dns)) {
33
+ throw new Error(`VpnConfig: invalid DNS address: ${dns}`);
34
+ }
35
+ }
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Validate a server config object. Throws on invalid config.
41
+ */
42
+ public static validateServerConfig(config: IVpnServerConfig): void {
43
+ if (!config.listenAddr) {
44
+ throw new Error('VpnConfig: listenAddr is required');
45
+ }
46
+ if (!config.privateKey) {
47
+ throw new Error('VpnConfig: privateKey is required');
48
+ }
49
+ if (!config.publicKey) {
50
+ throw new Error('VpnConfig: publicKey is required');
51
+ }
52
+ if (!config.subnet) {
53
+ throw new Error('VpnConfig: subnet is required');
54
+ }
55
+ if (!VpnConfig.isValidSubnet(config.subnet)) {
56
+ throw new Error(`VpnConfig: invalid subnet: ${config.subnet}`);
57
+ }
58
+ if (config.mtu !== undefined && (config.mtu < 576 || config.mtu > 65535)) {
59
+ throw new Error('VpnConfig: mtu must be between 576 and 65535');
60
+ }
61
+ if (config.keepaliveIntervalSecs !== undefined && config.keepaliveIntervalSecs < 1) {
62
+ throw new Error('VpnConfig: keepaliveIntervalSecs must be >= 1');
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Load a config from a JSON file.
68
+ */
69
+ public static async loadFromFile<T>(filePath: string): Promise<T> {
70
+ const content = await plugins.fs.promises.readFile(filePath, 'utf-8');
71
+ return JSON.parse(content) as T;
72
+ }
73
+
74
+ /**
75
+ * Save a config to a JSON file.
76
+ */
77
+ public static async saveToFile<T>(filePath: string, config: T): Promise<void> {
78
+ const content = JSON.stringify(config, null, 2);
79
+ await plugins.fs.promises.writeFile(filePath, content, 'utf-8');
80
+ }
81
+
82
+ /**
83
+ * Basic IP address validation.
84
+ */
85
+ private static isValidIp(ip: string): boolean {
86
+ const parts = ip.split('.');
87
+ if (parts.length !== 4) return false;
88
+ return parts.every((part) => {
89
+ const num = parseInt(part, 10);
90
+ return !isNaN(num) && num >= 0 && num <= 255 && String(num) === part;
91
+ });
92
+ }
93
+
94
+ /**
95
+ * Basic subnet validation (CIDR notation).
96
+ */
97
+ private static isValidSubnet(subnet: string): boolean {
98
+ const [ip, prefix] = subnet.split('/');
99
+ if (!ip || !prefix) return false;
100
+ if (!VpnConfig.isValidIp(ip)) return false;
101
+ const prefixNum = parseInt(prefix, 10);
102
+ return !isNaN(prefixNum) && prefixNum >= 0 && prefixNum <= 32;
103
+ }
104
+ }
@@ -0,0 +1,126 @@
1
+ import * as plugins from './smartvpn.plugins.js';
2
+ import type { TVpnPlatform, IVpnServiceUnit } from './smartvpn.interfaces.js';
3
+
4
+ /**
5
+ * Install the smartvpn daemon as a system service.
6
+ */
7
+ export class VpnInstaller {
8
+ /**
9
+ * Detect the current platform.
10
+ */
11
+ public static detectPlatform(): TVpnPlatform {
12
+ switch (process.platform) {
13
+ case 'linux':
14
+ return 'linux';
15
+ case 'darwin':
16
+ return 'macos';
17
+ case 'win32':
18
+ return 'windows';
19
+ default:
20
+ return 'unknown';
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Generate a systemd unit file for Linux.
26
+ */
27
+ public static generateSystemdUnit(options: {
28
+ binaryPath: string;
29
+ socketPath: string;
30
+ mode: 'client' | 'server';
31
+ configPath?: string;
32
+ description?: string;
33
+ }): IVpnServiceUnit {
34
+ const desc = options.description || `SmartVPN ${options.mode} daemon`;
35
+ const content = `[Unit]
36
+ Description=${desc}
37
+ After=network-online.target
38
+ Wants=network-online.target
39
+
40
+ [Service]
41
+ Type=simple
42
+ ExecStart=${options.binaryPath} --management-socket ${options.socketPath} --mode ${options.mode}
43
+ Restart=always
44
+ RestartSec=5
45
+ LimitNOFILE=65535
46
+
47
+ # Security hardening
48
+ NoNewPrivileges=no
49
+ ProtectSystem=strict
50
+ ProtectHome=yes
51
+ ReadWritePaths=/var/run /dev/net/tun
52
+ PrivateTmp=yes
53
+
54
+ [Install]
55
+ WantedBy=multi-user.target
56
+ `;
57
+
58
+ return {
59
+ platform: 'linux',
60
+ content,
61
+ installPath: `/etc/systemd/system/smartvpn-${options.mode}.service`,
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Generate a launchd plist for macOS.
67
+ */
68
+ public static generateLaunchdPlist(options: {
69
+ binaryPath: string;
70
+ socketPath: string;
71
+ mode: 'client' | 'server';
72
+ description?: string;
73
+ }): IVpnServiceUnit {
74
+ const label = `rocks.push.smartvpn.${options.mode}`;
75
+ const content = `<?xml version="1.0" encoding="UTF-8"?>
76
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
77
+ <plist version="1.0">
78
+ <dict>
79
+ <key>Label</key>
80
+ <string>${label}</string>
81
+ <key>ProgramArguments</key>
82
+ <array>
83
+ <string>${options.binaryPath}</string>
84
+ <string>--management-socket</string>
85
+ <string>${options.socketPath}</string>
86
+ <string>--mode</string>
87
+ <string>${options.mode}</string>
88
+ </array>
89
+ <key>RunAtLoad</key>
90
+ <true/>
91
+ <key>KeepAlive</key>
92
+ <true/>
93
+ <key>StandardErrorPath</key>
94
+ <string>/var/log/smartvpn-${options.mode}.err.log</string>
95
+ <key>StandardOutPath</key>
96
+ <string>/var/log/smartvpn-${options.mode}.out.log</string>
97
+ </dict>
98
+ </plist>
99
+ `;
100
+
101
+ return {
102
+ platform: 'macos',
103
+ content,
104
+ installPath: `/Library/LaunchDaemons/${label}.plist`,
105
+ };
106
+ }
107
+
108
+ /**
109
+ * Generate the appropriate service unit for the current platform.
110
+ */
111
+ public static generateServiceUnit(options: {
112
+ binaryPath: string;
113
+ socketPath: string;
114
+ mode: 'client' | 'server';
115
+ }): IVpnServiceUnit {
116
+ const platform = VpnInstaller.detectPlatform();
117
+ switch (platform) {
118
+ case 'linux':
119
+ return VpnInstaller.generateSystemdUnit(options);
120
+ case 'macos':
121
+ return VpnInstaller.generateLaunchdPlist(options);
122
+ default:
123
+ throw new Error(`VpnInstaller: unsupported platform: ${platform}`);
124
+ }
125
+ }
126
+ }
@@ -0,0 +1,107 @@
1
+ import * as plugins from './smartvpn.plugins.js';
2
+ import { VpnBridge } from './smartvpn.classes.vpnbridge.js';
3
+ import type {
4
+ IVpnServerOptions,
5
+ IVpnServerConfig,
6
+ IVpnStatus,
7
+ IVpnServerStatistics,
8
+ IVpnClientInfo,
9
+ IVpnKeypair,
10
+ TVpnServerCommands,
11
+ } from './smartvpn.interfaces.js';
12
+
13
+ /**
14
+ * VPN Server — manages a smartvpn daemon in server mode.
15
+ */
16
+ export class VpnServer extends plugins.events.EventEmitter {
17
+ private bridge: VpnBridge<TVpnServerCommands>;
18
+ private options: IVpnServerOptions;
19
+
20
+ constructor(options: IVpnServerOptions) {
21
+ super();
22
+ this.options = options;
23
+ this.bridge = new VpnBridge<TVpnServerCommands>({
24
+ transport: options.transport,
25
+ mode: 'server',
26
+ });
27
+
28
+ // Forward bridge events
29
+ this.bridge.on('exit', (code: number | null, signal: string | null) => {
30
+ this.emit('exit', { code, signal });
31
+ });
32
+ this.bridge.on('reconnected', () => {
33
+ this.emit('reconnected');
34
+ });
35
+ }
36
+
37
+ /**
38
+ * Start the daemon bridge (spawn or connect).
39
+ */
40
+ public async start(config?: IVpnServerConfig): Promise<void> {
41
+ const started = await this.bridge.start();
42
+ if (!started) {
43
+ throw new Error('VpnServer: failed to start daemon bridge');
44
+ }
45
+ const cfg = config || this.options.config;
46
+ if (cfg) {
47
+ await this.bridge.sendCommand('start', { config: cfg });
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Stop the VPN server.
53
+ */
54
+ public async stopServer(): Promise<void> {
55
+ await this.bridge.sendCommand('stop', {} as Record<string, never>);
56
+ }
57
+
58
+ /**
59
+ * Get server status.
60
+ */
61
+ public async getStatus(): Promise<IVpnStatus> {
62
+ return this.bridge.sendCommand('getStatus', {} as Record<string, never>);
63
+ }
64
+
65
+ /**
66
+ * Get server statistics.
67
+ */
68
+ public async getStatistics(): Promise<IVpnServerStatistics> {
69
+ return this.bridge.sendCommand('getStatistics', {} as Record<string, never>);
70
+ }
71
+
72
+ /**
73
+ * List connected clients.
74
+ */
75
+ public async listClients(): Promise<IVpnClientInfo[]> {
76
+ const result = await this.bridge.sendCommand('listClients', {} as Record<string, never>);
77
+ return result.clients;
78
+ }
79
+
80
+ /**
81
+ * Disconnect a specific client.
82
+ */
83
+ public async disconnectClient(clientId: string): Promise<void> {
84
+ await this.bridge.sendCommand('disconnectClient', { clientId });
85
+ }
86
+
87
+ /**
88
+ * Generate a new Noise keypair.
89
+ */
90
+ public async generateKeypair(): Promise<IVpnKeypair> {
91
+ return this.bridge.sendCommand('generateKeypair', {} as Record<string, never>);
92
+ }
93
+
94
+ /**
95
+ * Stop the daemon bridge.
96
+ */
97
+ public stop(): void {
98
+ this.bridge.stop();
99
+ }
100
+
101
+ /**
102
+ * Whether the bridge is running.
103
+ */
104
+ public get running(): boolean {
105
+ return this.bridge.running;
106
+ }
107
+ }
@@ -0,0 +1,166 @@
1
+ // ============================================================================
2
+ // Transport options
3
+ // ============================================================================
4
+
5
+ export interface IVpnTransportStdio {
6
+ transport: 'stdio';
7
+ }
8
+
9
+ export interface IVpnTransportSocket {
10
+ transport: 'socket';
11
+ socketPath: string;
12
+ autoReconnect?: boolean;
13
+ reconnectBaseDelayMs?: number;
14
+ reconnectMaxDelayMs?: number;
15
+ maxReconnectAttempts?: number;
16
+ }
17
+
18
+ export type TVpnTransportOptions = IVpnTransportStdio | IVpnTransportSocket;
19
+
20
+ // ============================================================================
21
+ // Client configuration
22
+ // ============================================================================
23
+
24
+ export interface IVpnClientConfig {
25
+ /** Server WebSocket URL, e.g. wss://vpn.example.com/tunnel */
26
+ serverUrl: string;
27
+ /** Server's static public key (base64) for Noise NK handshake */
28
+ serverPublicKey: string;
29
+ /** Optional DNS servers to use while connected */
30
+ dns?: string[];
31
+ /** Optional MTU for the TUN device */
32
+ mtu?: number;
33
+ /** Keepalive interval in seconds (default: 30) */
34
+ keepaliveIntervalSecs?: number;
35
+ }
36
+
37
+ export interface IVpnClientOptions {
38
+ transport: TVpnTransportOptions;
39
+ config?: IVpnClientConfig;
40
+ }
41
+
42
+ // ============================================================================
43
+ // Server configuration
44
+ // ============================================================================
45
+
46
+ export interface IVpnServerConfig {
47
+ /** Listen address for WebSocket, e.g. 0.0.0.0:443 */
48
+ listenAddr: string;
49
+ /** TLS certificate PEM (optional — can be behind reverse proxy) */
50
+ tlsCert?: string;
51
+ /** TLS private key PEM */
52
+ tlsKey?: string;
53
+ /** Server's Noise static private key (base64) */
54
+ privateKey: string;
55
+ /** Server's Noise static public key (base64) */
56
+ publicKey: string;
57
+ /** IP subnet for VPN clients, e.g. 10.8.0.0/24 */
58
+ subnet: string;
59
+ /** DNS servers pushed to clients */
60
+ dns?: string[];
61
+ /** MTU for TUN device */
62
+ mtu?: number;
63
+ /** Keepalive interval in seconds (default: 30) */
64
+ keepaliveIntervalSecs?: number;
65
+ /** Enable NAT/masquerade for client traffic */
66
+ enableNat?: boolean;
67
+ }
68
+
69
+ export interface IVpnServerOptions {
70
+ transport: TVpnTransportOptions;
71
+ config?: IVpnServerConfig;
72
+ }
73
+
74
+ // ============================================================================
75
+ // Status and statistics
76
+ // ============================================================================
77
+
78
+ export type TVpnConnectionState =
79
+ | 'disconnected'
80
+ | 'connecting'
81
+ | 'handshaking'
82
+ | 'connected'
83
+ | 'reconnecting'
84
+ | 'error';
85
+
86
+ export interface IVpnStatus {
87
+ state: TVpnConnectionState;
88
+ assignedIp?: string;
89
+ serverAddr?: string;
90
+ connectedSince?: string;
91
+ lastError?: string;
92
+ }
93
+
94
+ export interface IVpnStatistics {
95
+ bytesSent: number;
96
+ bytesReceived: number;
97
+ packetsSent: number;
98
+ packetsReceived: number;
99
+ keepalivesSent: number;
100
+ keepalivesReceived: number;
101
+ uptimeSeconds: number;
102
+ }
103
+
104
+ export interface IVpnClientInfo {
105
+ clientId: string;
106
+ assignedIp: string;
107
+ connectedSince: string;
108
+ bytesSent: number;
109
+ bytesReceived: number;
110
+ }
111
+
112
+ export interface IVpnServerStatistics extends IVpnStatistics {
113
+ activeClients: number;
114
+ totalConnections: number;
115
+ }
116
+
117
+ export interface IVpnKeypair {
118
+ publicKey: string;
119
+ privateKey: string;
120
+ }
121
+
122
+ // ============================================================================
123
+ // IPC Command maps (used by smartrust RustBridge<TCommands>)
124
+ // ============================================================================
125
+
126
+ export type TVpnClientCommands = {
127
+ connect: { params: { config: IVpnClientConfig }; result: { assignedIp: string } };
128
+ disconnect: { params: Record<string, never>; result: void };
129
+ getStatus: { params: Record<string, never>; result: IVpnStatus };
130
+ getStatistics: { params: Record<string, never>; result: IVpnStatistics };
131
+ };
132
+
133
+ export type TVpnServerCommands = {
134
+ start: { params: { config: IVpnServerConfig }; result: void };
135
+ stop: { params: Record<string, never>; result: void };
136
+ getStatus: { params: Record<string, never>; result: IVpnStatus };
137
+ getStatistics: { params: Record<string, never>; result: IVpnServerStatistics };
138
+ listClients: { params: Record<string, never>; result: { clients: IVpnClientInfo[] } };
139
+ disconnectClient: { params: { clientId: string }; result: void };
140
+ generateKeypair: { params: Record<string, never>; result: IVpnKeypair };
141
+ };
142
+
143
+ // ============================================================================
144
+ // Installer
145
+ // ============================================================================
146
+
147
+ export type TVpnPlatform = 'linux' | 'macos' | 'windows' | 'unknown';
148
+
149
+ export interface IVpnServiceUnit {
150
+ platform: TVpnPlatform;
151
+ content: string;
152
+ installPath: string;
153
+ }
154
+
155
+ // ============================================================================
156
+ // Events emitted by VpnClient / VpnServer
157
+ // ============================================================================
158
+
159
+ export interface IVpnEventMap {
160
+ 'status': IVpnStatus;
161
+ 'error': { message: string; code?: string };
162
+ 'client-connected': IVpnClientInfo;
163
+ 'client-disconnected': { clientId: string; reason?: string };
164
+ 'exit': { code: number | null; signal: string | null };
165
+ 'reconnected': void;
166
+ }
@@ -0,0 +1,6 @@
1
+ import * as plugins from './smartvpn.plugins.js';
2
+
3
+ export const packageDir = plugins.path.join(
4
+ plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url),
5
+ '../',
6
+ );
@@ -0,0 +1,14 @@
1
+ // node native
2
+ import * as path from 'path';
3
+ import * as fs from 'fs';
4
+ import * as os from 'os';
5
+ import * as url from 'url';
6
+ import * as events from 'events';
7
+
8
+ export { path, fs, os, url, events };
9
+
10
+ // @push.rocks
11
+ import * as smartpath from '@push.rocks/smartpath';
12
+ import * as smartrust from '@push.rocks/smartrust';
13
+
14
+ export { smartpath, smartrust };