import * as plugins from './smartvpn.plugins.js'; import { VpnBridge } from './smartvpn.classes.vpnbridge.js'; import type { IVpnServerOptions, IVpnServerConfig, IVpnStatus, IVpnServerStatistics, IVpnClientInfo, IVpnKeypair, IVpnClientTelemetry, IWgPeerConfig, IWgPeerInfo, IClientEntry, IClientConfigBundle, TVpnServerCommands, } from './smartvpn.interfaces.js'; /** * VPN Server — manages a smartvpn daemon in server mode. */ export class VpnServer extends plugins.events.EventEmitter { private bridge: VpnBridge; private options: IVpnServerOptions; constructor(options: IVpnServerOptions) { super(); this.options = options; this.bridge = new VpnBridge({ transport: options.transport, mode: 'server', }); // Forward bridge events this.bridge.on('exit', (code: number | null, signal: string | null) => { this.emit('exit', { code, signal }); }); this.bridge.on('reconnected', () => { this.emit('reconnected'); }); } /** * Start the daemon bridge (spawn or connect). */ public async start(config?: IVpnServerConfig): Promise { const started = await this.bridge.start(); if (!started) { throw new Error('VpnServer: failed to start daemon bridge'); } const cfg = config || this.options.config; if (cfg) { await this.bridge.sendCommand('start', { config: cfg }); } } /** * Stop the VPN server. */ public async stopServer(): Promise { await this.bridge.sendCommand('stop', {} as Record); } /** * Get server status. */ public async getStatus(): Promise { return this.bridge.sendCommand('getStatus', {} as Record); } /** * Get server statistics. */ public async getStatistics(): Promise { return this.bridge.sendCommand('getStatistics', {} as Record); } /** * List connected clients. */ public async listClients(): Promise { const result = await this.bridge.sendCommand('listClients', {} as Record); return result.clients; } /** * Disconnect a specific client. */ public async disconnectClient(clientId: string): Promise { await this.bridge.sendCommand('disconnectClient', { clientId }); } /** * Generate a new Noise keypair. */ public async generateKeypair(): Promise { return this.bridge.sendCommand('generateKeypair', {} as Record); } /** * Set rate limit for a specific client. */ public async setClientRateLimit( clientId: string, rateBytesPerSec: number, burstBytes: number, ): Promise { await this.bridge.sendCommand('setClientRateLimit', { clientId, rateBytesPerSec, burstBytes, }); } /** * Remove rate limit for a specific client (unlimited). */ public async removeClientRateLimit(clientId: string): Promise { await this.bridge.sendCommand('removeClientRateLimit', { clientId }); } /** * Get telemetry for a specific client. */ public async getClientTelemetry(clientId: string): Promise { return this.bridge.sendCommand('getClientTelemetry', { clientId }); } /** * Generate a WireGuard-compatible X25519 keypair. */ public async generateWgKeypair(): Promise { return this.bridge.sendCommand('generateWgKeypair', {} as Record); } /** * Add a WireGuard peer (server must be running in wireguard mode). */ public async addWgPeer(peer: IWgPeerConfig): Promise { await this.bridge.sendCommand('addWgPeer', { peer }); } /** * Remove a WireGuard peer by public key. */ public async removeWgPeer(publicKey: string): Promise { await this.bridge.sendCommand('removeWgPeer', { publicKey }); } /** * List WireGuard peers with stats. */ public async listWgPeers(): Promise { const result = await this.bridge.sendCommand('listWgPeers', {} as Record); return result.peers; } // ── Client Registry (Hub) Methods ───────────────────────────────────── /** * Create a new client. Generates keypairs, assigns IP, returns full config bundle. * The secrets (private keys) are only returned at creation time. */ public async createClient(opts: Partial): Promise { return this.bridge.sendCommand('createClient', { client: opts }); } /** * Remove a registered client (also disconnects if connected). */ public async removeClient(clientId: string): Promise { await this.bridge.sendCommand('removeClient', { clientId }); } /** * Get a registered client by ID. */ public async getClient(clientId: string): Promise { return this.bridge.sendCommand('getClient', { clientId }); } /** * List all registered clients. */ public async listRegisteredClients(): Promise { const result = await this.bridge.sendCommand('listRegisteredClients', {} as Record); return result.clients; } /** * Update a registered client's fields (ACLs, tags, description, etc.). */ public async updateClient(clientId: string, update: Partial): Promise { await this.bridge.sendCommand('updateClient', { clientId, update }); } /** * Enable a previously disabled client. */ public async enableClient(clientId: string): Promise { await this.bridge.sendCommand('enableClient', { clientId }); } /** * Disable a client (also disconnects if connected). */ public async disableClient(clientId: string): Promise { await this.bridge.sendCommand('disableClient', { clientId }); } /** * Rotate a client's keys. Returns a new config bundle with fresh keypairs. */ public async rotateClientKey(clientId: string): Promise { return this.bridge.sendCommand('rotateClientKey', { clientId }); } /** * Export a client config (without secrets) in the specified format. */ public async exportClientConfig(clientId: string, format: 'smartvpn' | 'wireguard'): Promise { const result = await this.bridge.sendCommand('exportClientConfig', { clientId, format }); return result.config; } /** * Generate a standalone Noise IK keypair (not tied to a client). */ public async generateClientKeypair(): Promise { return this.bridge.sendCommand('generateClientKeypair', {} as Record); } /** * Stop the daemon bridge. */ public stop(): void { this.bridge.stop(); } /** * Whether the bridge is running. */ public get running(): boolean { return this.bridge.running; } }