@serve.zone/dcrouter 11.13.0 → 11.15.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.
@@ -287,8 +287,8 @@ let OpsViewVpn = (() => {
287
287
  ? html `<span class="statusBadge enabled">enabled</span>`
288
288
  : html `<span class="statusBadge disabled">disabled</span>`,
289
289
  'VPN IP': client.assignedIp || '-',
290
- 'Tags': client.tags?.length
291
- ? html `${client.tags.map(t => html `<span class="tagBadge">${t}</span>`)}`
290
+ 'Tags': client.serverDefinedClientTags?.length
291
+ ? html `${client.serverDefinedClientTags.map(t => html `<span class="tagBadge">${t}</span>`)}`
292
292
  : '-',
293
293
  'Description': client.description || '-',
294
294
  'Created': new Date(client.createdAt).toLocaleDateString(),
@@ -344,11 +344,11 @@ let OpsViewVpn = (() => {
344
344
  action: async (modal) => {
345
345
  const form = modal.shadowRoot.querySelector('dees-form');
346
346
  const data = await form.collectFormData();
347
- const tags = data.tags ? data.tags.split(',').map((t) => t.trim()).filter(Boolean) : undefined;
347
+ const serverDefinedClientTags = data.tags ? data.tags.split(',').map((t) => t.trim()).filter(Boolean) : undefined;
348
348
  await appstate.vpnStatePart.dispatchAction(appstate.createVpnClientAction, {
349
349
  clientId: data.clientId,
350
350
  description: data.description || undefined,
351
- tags,
351
+ serverDefinedClientTags,
352
352
  });
353
353
  modal.destroy();
354
354
  },
@@ -366,4 +366,4 @@ let OpsViewVpn = (() => {
366
366
  return OpsViewVpn = _classThis;
367
367
  })();
368
368
  export { OpsViewVpn };
369
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BzLXZpZXctdnBuLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHNfd2ViL2VsZW1lbnRzL29wcy12aWV3LXZwbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsT0FBTyxFQUNMLFdBQVcsRUFDWCxJQUFJLEVBQ0osYUFBYSxFQUViLEdBQUcsRUFDSCxLQUFLLEVBQ0wsVUFBVSxHQUNYLE1BQU0sNkJBQTZCLENBQUM7QUFDckMsT0FBTyxLQUFLLFFBQVEsTUFBTSxnQkFBZ0IsQ0FBQztBQUMzQyxPQUFPLEtBQUssVUFBVSxNQUFNLG1DQUFtQyxDQUFDO0FBQ2hFLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUM5QyxPQUFPLEVBQW1CLE1BQU0sNkJBQTZCLENBQUM7SUFTakQsVUFBVTs0QkFEdEIsYUFBYSxDQUFDLGNBQWMsQ0FBQzs7OztzQkFDRSxXQUFXOzs7OzBCQUFuQixTQUFRLFdBQVc7Ozs7b0NBQ3hDLEtBQUssRUFBRTtZQUNSLDZLQUFTLFFBQVEsNkJBQVIsUUFBUSwyRkFBeUQ7WUFGNUUsNktBb1RDOzs7O1FBbFRDLDZFQUF3QyxRQUFRLENBQUMsWUFBWSxDQUFDLFFBQVEsRUFBRyxFQUFDO1FBQTFFLElBQVMsUUFBUSw4Q0FBeUQ7UUFBMUUsSUFBUyxRQUFRLG9EQUF5RDtRQUUxRTtZQUNFLEtBQUssRUFBRSxDQUFDOztZQUNSLE1BQU0sR0FBRyxHQUFHLFFBQVEsQ0FBQyxZQUFZLENBQUMsTUFBTSxFQUFFLENBQUMsU0FBUyxDQUFDLENBQUMsUUFBUSxFQUFFLEVBQUU7Z0JBQ2hFLElBQUksQ0FBQyxRQUFRLEdBQUcsUUFBUSxDQUFDO1lBQzNCLENBQUMsQ0FBQyxDQUFDO1lBQ0gsSUFBSSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7U0FDaEM7UUFFRCxLQUFLLENBQUMsaUJBQWlCO1lBQ3JCLE1B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
369
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BzLXZpZXctdnBuLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vdHNfd2ViL2VsZW1lbnRzL29wcy12aWV3LXZwbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsT0FBTyxFQUNMLFdBQVcsRUFDWCxJQUFJLEVBQ0osYUFBYSxFQUViLEdBQUcsRUFDSCxLQUFLLEVBQ0wsVUFBVSxHQUNYLE1BQU0sNkJBQTZCLENBQUM7QUFDckMsT0FBTyxLQUFLLFFBQVEsTUFBTSxnQkFBZ0IsQ0FBQztBQUMzQyxPQUFPLEtBQUssVUFBVSxNQUFNLG1DQUFtQyxDQUFDO0FBQ2hFLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUM5QyxPQUFPLEVBQW1CLE1BQU0sNkJBQTZCLENBQUM7SUFTakQsVUFBVTs0QkFEdEIsYUFBYSxDQUFDLGNBQWMsQ0FBQzs7OztzQkFDRSxXQUFXOzs7OzBCQUFuQixTQUFRLFdBQVc7Ozs7b0NBQ3hDLEtBQUssRUFBRTtZQUNSLDZLQUFTLFFBQVEsNkJBQVIsUUFBUSwyRkFBeUQ7WUFGNUUsNktBb1RDOzs7O1FBbFRDLDZFQUF3QyxRQUFRLENBQUMsWUFBWSxDQUFDLFFBQVEsRUFBRyxFQUFDO1FBQTFFLElBQVMsUUFBUSw4Q0FBeUQ7UUFBMUUsSUFBUyxRQUFRLG9EQUF5RDtRQUUxRTtZQUNFLEtBQUssRUFBRSxDQUFDOztZQUNSLE1BQU0sR0FBRyxHQUFHLFFBQVEsQ0FBQyxZQUFZLENBQUMsTUFBTSxFQUFFLENBQUMsU0FBUyxDQUFDLENBQUMsUUFBUSxFQUFFLEVBQUU7Z0JBQ2hFLElBQUksQ0FBQyxRQUFRLEdBQUcsUUFBUSxDQUFDO1lBQzNCLENBQUMsQ0FBQyxDQUFDO1lBQ0gsSUFBSSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7U0FDaEM7UUFFRCxLQUFLLENBQUMsaUJBQWlCO1lBQ3JCLE1B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
@@ -1,4 +1,4 @@
1
- export declare const validViews: readonly ["overview", "network", "emails", "logs", "routes", "apitokens", "configuration", "security", "certificates", "remoteingress"];
1
+ export declare const validViews: readonly ["overview", "network", "emails", "logs", "routes", "apitokens", "configuration", "security", "certificates", "remoteingress", "vpn"];
2
2
  export type TValidView = typeof validViews[number];
3
3
  declare class AppRouter {
4
4
  private router;
@@ -1,7 +1,7 @@
1
1
  import * as plugins from './plugins.js';
2
2
  import * as appstate from './appstate.js';
3
3
  const SmartRouter = plugins.domtools.plugins.smartrouter.SmartRouter;
4
- export const validViews = ['overview', 'network', 'emails', 'logs', 'routes', 'apitokens', 'configuration', 'security', 'certificates', 'remoteingress'];
4
+ export const validViews = ['overview', 'network', 'emails', 'logs', 'routes', 'apitokens', 'configuration', 'security', 'certificates', 'remoteingress', 'vpn'];
5
5
  class AppRouter {
6
6
  router;
7
7
  initialized = false;
@@ -88,4 +88,4 @@ class AppRouter {
88
88
  }
89
89
  }
90
90
  export const appRouter = new AppRouter();
91
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHNfd2ViL3JvdXRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUN4QyxPQUFPLEtBQUssUUFBUSxNQUFNLGVBQWUsQ0FBQztBQUUxQyxNQUFNLFdBQVcsR0FBRyxPQUFPLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsV0FBVyxDQUFDO0FBRXJFLE1BQU0sQ0FBQyxNQUFNLFVBQVUsR0FBRyxDQUFDLFVBQVUsRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxRQUFRLEVBQUUsV0FBVyxFQUFFLGVBQWUsRUFBRSxVQUFVLEVBQUUsY0FBYyxFQUFFLGVBQWUsQ0FBVSxDQUFDO0FBSWxLLE1BQU0sU0FBUztJQUNMLE1BQU0sQ0FBbUM7SUFDekMsV0FBVyxHQUFHLEtBQUssQ0FBQztJQUNwQixtQkFBbUIsR0FBRyxLQUFLLENBQUM7SUFFcEM7UUFDRSxJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksV0FBVyxDQUFDLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxDQUFDLENBQUM7SUFDbEQsQ0FBQztJQUVNLElBQUk7UUFDVCxJQUFJLElBQUksQ0FBQyxXQUFXO1lBQUUsT0FBTztRQUM3QixJQUFJLENBQUMsV0FBVyxFQUFFLENBQUM7UUFDbkIsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO1FBQ3RCLElBQUksQ0FBQyxrQkFBa0IsRUFBRSxDQUFDO1FBQzFCLElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDO0lBQzFCLENBQUM7SUFFTyxXQUFXO1FBQ2pCLEtBQUssTUFBTSxJQUFJLElBQUksVUFBVSxFQUFFLENBQUM7WUFDOUIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsSUFBSSxJQUFJLEVBQUUsRUFBRSxLQUFLLElBQUksRUFBRTtnQkFDcEMsSUFBSSxDQUFDLGVBQWUsQ0FB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
91
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHNfd2ViL3JvdXRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUN4QyxPQUFPLEtBQUssUUFBUSxNQUFNLGVBQWUsQ0FBQztBQUUxQyxNQUFNLFdBQVcsR0FBRyxPQUFPLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsV0FBVyxDQUFDO0FBRXJFLE1BQU0sQ0FBQyxNQUFNLFVBQVUsR0FBRyxDQUFDLFVBQVUsRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxRQUFRLEVBQUUsV0FBVyxFQUFFLGVBQWUsRUFBRSxVQUFVLEVBQUUsY0FBYyxFQUFFLGVBQWUsRUFBRSxLQUFLLENBQVUsQ0FBQztBQUl6SyxNQUFNLFNBQVM7SUFDTCxNQUFNLENBQW1DO0lBQ3pDLFdBQVcsR0FBRyxLQUFLLENBQUM7SUFDcEIsbUJBQW1CLEdBQUcsS0FBSyxDQUFDO0lBRXBDO1FBQ0UsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLFdBQVcsQ0FBQyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDO0lBQ2xELENBQUM7SUFFTSxJQUFJO1FBQ1QsSUFBSSxJQUFJLENBQUMsV0FBVztZQUFFLE9BQU87UUFDN0IsSUFBSSxDQUFDLFdBQVcsRUFBRSxDQUFDO1FBQ25CLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztRQUN0QixJQUFJLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztRQUMxQixJQUFJLENBQUMsV0FBVyxHQUFHLElBQUksQ0FBQztJQUMxQixDQUFDO0lBRU8sV0FBVztRQUNqQixLQUFLLE1BQU0sSUFBSSxJQUFJLFVBQVUsRUFBRSxDQUFDO1lBQzlCLElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLElBQUksSUFBSSxFQUFFLEVBQUUsS0FBSyxJQUFJLEVBQUU7Z0JBQ3BDLElBQUksQ0FB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
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@serve.zone/dcrouter",
3
3
  "private": false,
4
- "version": "11.13.0",
4
+ "version": "11.15.0",
5
5
  "description": "A multifaceted routing service handling mail and SMS delivery functions.",
6
6
  "type": "module",
7
7
  "exports": {
@@ -59,7 +59,7 @@
59
59
  "@push.rocks/smartrx": "^3.0.10",
60
60
  "@push.rocks/smartstate": "^2.3.0",
61
61
  "@push.rocks/smartunique": "^3.0.9",
62
- "@push.rocks/smartvpn": "1.12.0",
62
+ "@push.rocks/smartvpn": "1.13.0",
63
63
  "@push.rocks/taskbuffer": "^8.0.2",
64
64
  "@serve.zone/catalog": "^2.9.0",
65
65
  "@serve.zone/interfaces": "^5.3.0",
package/readme.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  **dcrouter: The all-in-one gateway for your datacenter.** 🚀
6
6
 
7
- A comprehensive traffic routing solution that provides unified gateway capabilities for HTTP/HTTPS, TCP/SNI, email (SMTP), DNS, RADIUS, and remote edge ingress — all from a single process. Designed for enterprises requiring robust traffic management, automatic TLS certificate provisioning, distributed edge networking, and enterprise-grade email infrastructure.
7
+ A comprehensive traffic routing solution that provides unified gateway capabilities for HTTP/HTTPS, TCP/SNI, email (SMTP), DNS, RADIUS, VPN, and remote edge ingress — all from a single process. Designed for enterprises requiring robust traffic management, automatic TLS certificate provisioning, VPN-based access control, distributed edge networking, and enterprise-grade email infrastructure.
8
8
 
9
9
  ## Issue Reporting and Security
10
10
 
@@ -23,6 +23,7 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
23
23
  - [DNS Server](#dns-server)
24
24
  - [RADIUS Server](#radius-server)
25
25
  - [Remote Ingress](#remote-ingress)
26
+ - [VPN Access Control](#vpn-access-control)
26
27
  - [Certificate Management](#certificate-management)
27
28
  - [Storage & Caching](#storage--caching)
28
29
  - [Security Features](#security-features)
@@ -73,6 +74,14 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
73
74
  - **Real-time status monitoring** — connected/disconnected state, public IP, active tunnels, heartbeat tracking
74
75
  - **OpsServer dashboard** with enable/disable, edit, secret regeneration, token copy, and delete actions
75
76
 
77
+ ### 🔐 VPN Access Control (powered by [smartvpn](https://code.foss.global/push.rocks/smartvpn))
78
+ - **WireGuard + native transports** — standard WireGuard clients (iOS, Android, macOS, Windows, Linux) plus custom WebSocket/QUIC tunnels
79
+ - **Route-level VPN gating** — mark any route with `vpn: { required: true }` to restrict access to VPN clients only
80
+ - **Rootless operation** — auto-detects privileges: kernel TUN when running as root, userspace NAT (smoltcp) when not
81
+ - **Client management** — create, enable, disable, rotate keys, export WireGuard `.conf` files via OpsServer API
82
+ - **IP-based enforcement** — VPN clients get IPs from a configurable subnet; SmartProxy enforces `ipAllowList` per route
83
+ - **PROXY protocol v2** — in socket mode, the NAT engine sends PP v2 on outbound connections to preserve VPN client identity
84
+
76
85
  ### ⚡ High Performance
77
86
  - **Rust-powered proxy engine** via SmartProxy for maximum throughput
78
87
  - **Rust-powered MTA engine** via smartmta (TypeScript + Rust hybrid) for reliable email delivery
@@ -89,7 +98,7 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
89
98
  ### 🖥️ OpsServer Dashboard
90
99
  - **Web-based management interface** with real-time monitoring
91
100
  - **JWT authentication** with session persistence
92
- - **Live views** for connections, email queues, DNS queries, RADIUS sessions, certificates, remote ingress edges, and security events
101
+ - **Live views** for connections, email queues, DNS queries, RADIUS sessions, certificates, remote ingress edges, VPN clients, and security events
93
102
  - **Domain-centric certificate overview** with backoff status and one-click reprovisioning
94
103
  - **Remote ingress management** with connection token generation and one-click copy
95
104
  - **Read-only configuration display** — DcRouter is configured through code
@@ -248,6 +257,13 @@ const router = new DcRouter({
248
257
  hubDomain: 'hub.example.com',
249
258
  },
250
259
 
260
+ // VPN — restrict sensitive routes to VPN clients
261
+ vpnConfig: {
262
+ enabled: true,
263
+ serverEndpoint: 'vpn.example.com',
264
+ wgListenPort: 51820,
265
+ },
266
+
251
267
  // Persistent storage
252
268
  storage: { fsPath: '/var/lib/dcrouter/data' },
253
269
 
@@ -276,6 +292,7 @@ graph TB
276
292
  DNS[DNS Queries]
277
293
  RAD[RADIUS Clients]
278
294
  EDGE[Edge Nodes]
295
+ VPN[VPN Clients]
279
296
  end
280
297
 
281
298
  subgraph "DcRouter Core"
@@ -285,6 +302,7 @@ graph TB
285
302
  DS[SmartDNS Server<br/><i>Rust-powered</i>]
286
303
  RS[SmartRadius Server]
287
304
  RI[RemoteIngress Hub<br/><i>Rust data plane</i>]
305
+ VS[SmartVPN Server<br/><i>Rust data plane</i>]
288
306
  CM[Certificate Manager<br/><i>smartacme v9</i>]
289
307
  OS[OpsServer Dashboard]
290
308
  MM[Metrics Manager]
@@ -305,12 +323,14 @@ graph TB
305
323
  DNS --> DS
306
324
  RAD --> RS
307
325
  EDGE --> RI
326
+ VPN --> VS
308
327
 
309
328
  DC --> SP
310
329
  DC --> ES
311
330
  DC --> DS
312
331
  DC --> RS
313
332
  DC --> RI
333
+ DC --> VS
314
334
  DC --> CM
315
335
  DC --> OS
316
336
  DC --> MM
@@ -428,6 +448,17 @@ interface IDcRouterOptions {
428
448
  };
429
449
  };
430
450
 
451
+ // ── VPN ───────────────────────────────────────────────────────
452
+ /** VPN server for route-level access control */
453
+ vpnConfig?: {
454
+ enabled?: boolean; // default: false
455
+ subnet?: string; // default: '10.8.0.0/24'
456
+ wgListenPort?: number; // default: 51820
457
+ dns?: string[]; // DNS servers pushed to VPN clients
458
+ serverEndpoint?: string; // Hostname in generated client configs
459
+ forwardingMode?: 'tun' | 'socket'; // default: auto-detect (root → tun, else socket)
460
+ };
461
+
431
462
  // ── HTTP/3 (QUIC) ────────────────────────────────────────────
432
463
  /** HTTP/3 config — enabled by default on qualifying HTTPS routes */
433
464
  http3?: {
@@ -975,6 +1006,78 @@ The OpsServer Remote Ingress view provides:
975
1006
  | **Copy Token** | Generate and copy a base64url connection token to clipboard |
976
1007
  | **Delete** | Remove the edge registration |
977
1008
 
1009
+ ## VPN Access Control
1010
+
1011
+ DcRouter integrates [`@push.rocks/smartvpn`](https://code.foss.global/push.rocks/smartvpn) to provide VPN-based route access control. VPN clients connect via standard WireGuard or native WebSocket/QUIC transports, receive an IP from a configurable subnet, and can then access routes that are restricted to VPN-only traffic.
1012
+
1013
+ ### How It Works
1014
+
1015
+ 1. **SmartVPN daemon** runs inside dcrouter with a Rust data plane (WireGuard via `boringtun`, custom protocol via Noise IK)
1016
+ 2. Clients connect and get assigned an IP from the VPN subnet (e.g. `10.8.0.0/24`)
1017
+ 3. Routes with `vpn: { required: true }` get `security.ipAllowList` automatically injected with the VPN subnet
1018
+ 4. SmartProxy enforces the allowlist — only VPN-sourced traffic is accepted on those routes
1019
+
1020
+ ### Two Operating Modes
1021
+
1022
+ | Mode | Root Required? | How It Works |
1023
+ |------|---------------|-------------|
1024
+ | **TUN** (`forwardingMode: 'tun'`) | Yes | Kernel TUN device — VPN traffic enters the network stack with real VPN IPs |
1025
+ | **Socket** (`forwardingMode: 'socket'`) | No | Userspace NAT via smoltcp — outbound connections send PROXY protocol v2 to preserve VPN client IPs |
1026
+
1027
+ DcRouter auto-detects: if running as root, it uses TUN mode; otherwise, it falls back to socket mode. You can override this with the `forwardingMode` option.
1028
+
1029
+ ### Configuration
1030
+
1031
+ ```typescript
1032
+ const router = new DcRouter({
1033
+ vpnConfig: {
1034
+ enabled: true,
1035
+ subnet: '10.8.0.0/24', // VPN client IP pool (default)
1036
+ wgListenPort: 51820, // WireGuard UDP port (default)
1037
+ serverEndpoint: 'vpn.example.com', // Hostname in generated client configs
1038
+ dns: ['1.1.1.1', '8.8.8.8'], // DNS servers pushed to clients
1039
+ // forwardingMode: 'socket', // Override auto-detection
1040
+ },
1041
+ smartProxyConfig: {
1042
+ routes: [
1043
+ // This route is VPN-only — non-VPN clients are blocked
1044
+ {
1045
+ name: 'admin-panel',
1046
+ match: { domains: ['admin.example.com'], ports: [443] },
1047
+ action: {
1048
+ type: 'forward',
1049
+ targets: [{ host: '192.168.1.50', port: 8080 }],
1050
+ tls: { mode: 'terminate', certificate: 'auto' },
1051
+ },
1052
+ vpn: { required: true }, // 🔐 Only VPN clients can access this
1053
+ },
1054
+ // This route is public — anyone can access it
1055
+ {
1056
+ name: 'public-site',
1057
+ match: { domains: ['example.com'], ports: [443] },
1058
+ action: {
1059
+ type: 'forward',
1060
+ targets: [{ host: '192.168.1.10', port: 80 }],
1061
+ tls: { mode: 'terminate', certificate: 'auto' },
1062
+ },
1063
+ },
1064
+ ],
1065
+ },
1066
+ });
1067
+ ```
1068
+
1069
+ ### Client Management via OpsServer API
1070
+
1071
+ Once the VPN server is running, you can manage clients through the OpsServer dashboard or API:
1072
+
1073
+ - **Create client** — generates WireGuard keypairs, assigns IP, returns a ready-to-use `.conf` file
1074
+ - **Enable / Disable** — toggle client access without deleting
1075
+ - **Rotate keys** — generate fresh keypairs (invalidates old ones)
1076
+ - **Export config** — re-export in WireGuard or SmartVPN format
1077
+ - **Telemetry** — per-client bytes sent/received, keepalives, rate limiting
1078
+
1079
+ Standard WireGuard clients on any platform (iOS, Android, macOS, Windows, Linux) can connect using the generated `.conf` file or QR code — no custom VPN software needed.
1080
+
978
1081
  ## Certificate Management
979
1082
 
980
1083
  DcRouter uses [`@push.rocks/smartacme`](https://code.foss.global/push.rocks/smartacme) v9 for ACME certificate provisioning. smartacme v9 brings significant improvements over previous versions:
@@ -1458,6 +1561,7 @@ The container exposes all service ports:
1458
1561
  | 1812, 1813 | UDP | RADIUS auth/acct |
1459
1562
  | 3000 | TCP | OpsServer dashboard |
1460
1563
  | 8443 | TCP | Remote ingress tunnels |
1564
+ | 51820 | UDP | WireGuard VPN |
1461
1565
  | 29000–30000 | TCP | Dynamic port range |
1462
1566
 
1463
1567
  ### Building the Image
@@ -1471,7 +1575,7 @@ The Docker build supports multi-platform (`linux/amd64`, `linux/arm64`) via [tsd
1471
1575
 
1472
1576
  ## License and Legal Information
1473
1577
 
1474
- This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [license](./license) file.
1578
+ This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./LICENSE) file.
1475
1579
 
1476
1580
  **Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
1477
1581
 
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/dcrouter',
6
- version: '11.13.0',
6
+ version: '11.15.0',
7
7
  description: 'A multifaceted routing service handling mail and SMS delivery functions.'
8
8
  }
@@ -208,6 +208,12 @@ export interface IDcRouterOptions {
208
208
  serverEndpoint?: string;
209
209
  /** Override forwarding mode. Default: auto-detect (tun if root, socket otherwise) */
210
210
  forwardingMode?: 'tun' | 'socket';
211
+ /** Pre-defined VPN clients created on startup */
212
+ clients?: Array<{
213
+ clientId: string;
214
+ serverDefinedClientTags?: string[];
215
+ description?: string;
216
+ }>;
211
217
  };
212
218
  }
213
219
 
@@ -453,7 +459,14 @@ export class DcRouter {
453
459
  () => this.getConstructorRoutes(),
454
460
  () => this.smartProxy,
455
461
  () => this.options.http3,
456
- () => this.options.vpnConfig?.enabled ? (this.options.vpnConfig.subnet || '10.8.0.0/24') : undefined,
462
+ this.options.vpnConfig?.enabled
463
+ ? (tags?: string[]) => {
464
+ if (tags?.length && this.vpnManager) {
465
+ return this.vpnManager.getClientIpsForServerDefinedTags(tags);
466
+ }
467
+ return [this.options.vpnConfig?.subnet || '10.8.0.0/24'];
468
+ }
469
+ : undefined,
457
470
  );
458
471
  this.apiTokenManager = new ApiTokenManager(this.storageManager);
459
472
  await this.apiTokenManager.initialize();
@@ -2086,6 +2099,11 @@ export class DcRouter {
2086
2099
  dns: this.options.vpnConfig.dns,
2087
2100
  serverEndpoint: this.options.vpnConfig.serverEndpoint,
2088
2101
  forwardingMode: this.options.vpnConfig.forwardingMode,
2102
+ initialClients: this.options.vpnConfig.clients,
2103
+ onClientChanged: () => {
2104
+ // Re-apply routes so tag-based ipAllowLists get updated
2105
+ this.routeConfigManager?.applyRoutes();
2106
+ },
2089
2107
  });
2090
2108
 
2091
2109
  await this.vpnManager.start();
@@ -2104,11 +2122,23 @@ export class DcRouter {
2104
2122
  if (dcrouterRoute.vpn?.required) {
2105
2123
  injectedCount++;
2106
2124
  const existing = route.security?.ipAllowList || [];
2125
+
2126
+ let vpnAllowList: string[];
2127
+ if (dcrouterRoute.vpn.allowedServerDefinedClientTags?.length && this.vpnManager) {
2128
+ // Tag-based: only specific client IPs
2129
+ vpnAllowList = this.vpnManager.getClientIpsForServerDefinedTags(
2130
+ dcrouterRoute.vpn.allowedServerDefinedClientTags,
2131
+ );
2132
+ } else {
2133
+ // No tags specified: entire VPN subnet
2134
+ vpnAllowList = [vpnSubnet];
2135
+ }
2136
+
2107
2137
  return {
2108
2138
  ...route,
2109
2139
  security: {
2110
2140
  ...route.security,
2111
- ipAllowList: [...existing, vpnSubnet],
2141
+ ipAllowList: [...existing, ...vpnAllowList],
2112
2142
  },
2113
2143
  };
2114
2144
  }
@@ -2116,7 +2146,7 @@ export class DcRouter {
2116
2146
  });
2117
2147
 
2118
2148
  if (injectedCount > 0) {
2119
- logger.log('info', `VPN: Injected ipAllowList (${vpnSubnet}) into ${injectedCount} VPN-protected route(s)`);
2149
+ logger.log('info', `VPN: Injected ipAllowList into ${injectedCount} VPN-protected route(s)`);
2120
2150
  }
2121
2151
 
2122
2152
  return result;
@@ -23,7 +23,7 @@ export class RouteConfigManager {
23
23
  private getHardcodedRoutes: () => plugins.smartproxy.IRouteConfig[],
24
24
  private getSmartProxy: () => plugins.smartproxy.SmartProxy | undefined,
25
25
  private getHttp3Config?: () => IHttp3Config | undefined,
26
- private getVpnSubnet?: () => string | undefined,
26
+ private getVpnAllowList?: (tags?: string[]) => string[],
27
27
  ) {}
28
28
 
29
29
  /**
@@ -246,7 +246,7 @@ export class RouteConfigManager {
246
246
  // Private: apply merged routes to SmartProxy
247
247
  // =========================================================================
248
248
 
249
- private async applyRoutes(): Promise<void> {
249
+ public async applyRoutes(): Promise<void> {
250
250
  const smartProxy = this.getSmartProxy();
251
251
  if (!smartProxy) return;
252
252
 
@@ -262,9 +262,9 @@ export class RouteConfigManager {
262
262
  enabledRoutes.push(route);
263
263
  }
264
264
 
265
- // Add enabled programmatic routes (with HTTP/3 augmentation if enabled)
265
+ // Add enabled programmatic routes (with HTTP/3 and VPN augmentation)
266
266
  const http3Config = this.getHttp3Config?.();
267
- const vpnSubnet = this.getVpnSubnet?.();
267
+ const vpnAllowList = this.getVpnAllowList;
268
268
  for (const stored of this.storedRoutes.values()) {
269
269
  if (stored.enabled) {
270
270
  let route = stored.route;
@@ -272,15 +272,16 @@ export class RouteConfigManager {
272
272
  route = augmentRouteWithHttp3(route, { enabled: true, ...http3Config });
273
273
  }
274
274
  // Inject VPN security for programmatic routes with vpn.required
275
- if (vpnSubnet) {
275
+ if (vpnAllowList) {
276
276
  const dcRoute = route as IDcRouterRouteConfig;
277
277
  if (dcRoute.vpn?.required) {
278
278
  const existing = route.security?.ipAllowList || [];
279
+ const allowList = vpnAllowList(dcRoute.vpn.allowedServerDefinedClientTags);
279
280
  route = {
280
281
  ...route,
281
282
  security: {
282
283
  ...route.security,
283
- ipAllowList: [...existing, vpnSubnet],
284
+ ipAllowList: [...existing, ...allowList],
284
285
  },
285
286
  };
286
287
  }
@@ -25,7 +25,7 @@ export class VpnHandler {
25
25
  const clients = manager.listClients().map((c) => ({
26
26
  clientId: c.clientId,
27
27
  enabled: c.enabled,
28
- tags: c.tags,
28
+ serverDefinedClientTags: c.serverDefinedClientTags,
29
29
  description: c.description,
30
30
  assignedIp: c.assignedIp,
31
31
  createdAt: c.createdAt,
@@ -89,7 +89,7 @@ export class VpnHandler {
89
89
  try {
90
90
  const bundle = await manager.createClient({
91
91
  clientId: dataArg.clientId,
92
- tags: dataArg.tags,
92
+ serverDefinedClientTags: dataArg.serverDefinedClientTags,
93
93
  description: dataArg.description,
94
94
  });
95
95
 
@@ -98,7 +98,7 @@ export class VpnHandler {
98
98
  client: {
99
99
  clientId: bundle.entry.clientId,
100
100
  enabled: bundle.entry.enabled ?? true,
101
- tags: bundle.entry.tags,
101
+ serverDefinedClientTags: bundle.entry.serverDefinedClientTags,
102
102
  description: bundle.entry.description,
103
103
  assignedIp: bundle.entry.assignedIp,
104
104
  createdAt: Date.now(),
@@ -16,6 +16,14 @@ export interface IVpnManagerConfig {
16
16
  serverEndpoint?: string;
17
17
  /** Override forwarding mode. Default: auto-detect (tun if root, socket otherwise) */
18
18
  forwardingMode?: 'tun' | 'socket';
19
+ /** Pre-defined VPN clients created on startup (idempotent — skips already-persisted clients) */
20
+ initialClients?: Array<{
21
+ clientId: string;
22
+ serverDefinedClientTags?: string[];
23
+ description?: string;
24
+ }>;
25
+ /** Called when clients are created/deleted/toggled — triggers route re-application */
26
+ onClientChanged?: () => void;
19
27
  }
20
28
 
21
29
  interface IPersistedServerKeys {
@@ -28,7 +36,7 @@ interface IPersistedServerKeys {
28
36
  interface IPersistedClient {
29
37
  clientId: string;
30
38
  enabled: boolean;
31
- tags?: string[];
39
+ serverDefinedClientTags?: string[];
32
40
  description?: string;
33
41
  assignedIp?: string;
34
42
  noisePublicKey: string;
@@ -36,6 +44,8 @@ interface IPersistedClient {
36
44
  createdAt: number;
37
45
  updatedAt: number;
38
46
  expiresAt?: string;
47
+ /** @deprecated Legacy field — migrated to serverDefinedClientTags on load */
48
+ tags?: string[];
39
49
  }
40
50
 
41
51
  /**
@@ -92,7 +102,7 @@ export class VpnManager {
92
102
  publicKey: client.noisePublicKey,
93
103
  wgPublicKey: client.wgPublicKey,
94
104
  enabled: client.enabled,
95
- tags: client.tags,
105
+ serverDefinedClientTags: client.serverDefinedClientTags,
96
106
  description: client.description,
97
107
  assignedIp: client.assignedIp,
98
108
  expiresAt: client.expiresAt,
@@ -122,6 +132,21 @@ export class VpnManager {
122
132
  };
123
133
 
124
134
  await this.vpnServer.start(serverConfig);
135
+
136
+ // Create initial clients from config (idempotent — skip already-persisted)
137
+ if (this.config.initialClients) {
138
+ for (const initial of this.config.initialClients) {
139
+ if (!this.clients.has(initial.clientId)) {
140
+ const bundle = await this.createClient({
141
+ clientId: initial.clientId,
142
+ serverDefinedClientTags: initial.serverDefinedClientTags,
143
+ description: initial.description,
144
+ });
145
+ logger.log('info', `VPN: Created initial client '${initial.clientId}' (IP: ${bundle.entry.assignedIp})`);
146
+ }
147
+ }
148
+ }
149
+
125
150
  logger.log('info', `VPN server started: mode=${this._forwardingMode}, subnet=${subnet}, wg=:${wgListenPort}, clients=${this.clients.size}`);
126
151
  }
127
152
 
@@ -148,7 +173,7 @@ export class VpnManager {
148
173
  */
149
174
  public async createClient(opts: {
150
175
  clientId: string;
151
- tags?: string[];
176
+ serverDefinedClientTags?: string[];
152
177
  description?: string;
153
178
  }): Promise<plugins.smartvpn.IClientConfigBundle> {
154
179
  if (!this.vpnServer) {
@@ -157,7 +182,7 @@ export class VpnManager {
157
182
 
158
183
  const bundle = await this.vpnServer.createClient({
159
184
  clientId: opts.clientId,
160
- tags: opts.tags,
185
+ serverDefinedClientTags: opts.serverDefinedClientTags,
161
186
  description: opts.description,
162
187
  });
163
188
 
@@ -174,7 +199,7 @@ export class VpnManager {
174
199
  const persisted: IPersistedClient = {
175
200
  clientId: bundle.entry.clientId,
176
201
  enabled: bundle.entry.enabled ?? true,
177
- tags: bundle.entry.tags,
202
+ serverDefinedClientTags: bundle.entry.serverDefinedClientTags,
178
203
  description: bundle.entry.description,
179
204
  assignedIp: bundle.entry.assignedIp,
180
205
  noisePublicKey: bundle.entry.publicKey,
@@ -186,6 +211,7 @@ export class VpnManager {
186
211
  this.clients.set(persisted.clientId, persisted);
187
212
  await this.persistClient(persisted);
188
213
 
214
+ this.config.onClientChanged?.();
189
215
  return bundle;
190
216
  }
191
217
 
@@ -199,6 +225,7 @@ export class VpnManager {
199
225
  await this.vpnServer.removeClient(clientId);
200
226
  this.clients.delete(clientId);
201
227
  await this.storageManager.delete(`${STORAGE_PREFIX_CLIENTS}${clientId}`);
228
+ this.config.onClientChanged?.();
202
229
  }
203
230
 
204
231
  /**
@@ -220,6 +247,7 @@ export class VpnManager {
220
247
  client.updatedAt = Date.now();
221
248
  await this.persistClient(client);
222
249
  }
250
+ this.config.onClientChanged?.();
223
251
  }
224
252
 
225
253
  /**
@@ -234,6 +262,7 @@ export class VpnManager {
234
262
  client.updatedAt = Date.now();
235
263
  await this.persistClient(client);
236
264
  }
265
+ this.config.onClientChanged?.();
237
266
  }
238
267
 
239
268
  /**
@@ -283,6 +312,22 @@ export class VpnManager {
283
312
  return config;
284
313
  }
285
314
 
315
+ // ── Tag-based access control ───────────────────────────────────────────
316
+
317
+ /**
318
+ * Get assigned IPs for all enabled clients matching any of the given server-defined tags.
319
+ */
320
+ public getClientIpsForServerDefinedTags(tags: string[]): string[] {
321
+ const ips: string[] = [];
322
+ for (const client of this.clients.values()) {
323
+ if (!client.enabled || !client.assignedIp) continue;
324
+ if (client.serverDefinedClientTags?.some(t => tags.includes(t))) {
325
+ ips.push(client.assignedIp);
326
+ }
327
+ }
328
+ return ips;
329
+ }
330
+
286
331
  // ── Status and telemetry ───────────────────────────────────────────────
287
332
 
288
333
  /**
@@ -364,6 +409,12 @@ export class VpnManager {
364
409
  for (const key of keys) {
365
410
  const client = await this.storageManager.getJSON<IPersistedClient>(key);
366
411
  if (client) {
412
+ // Migrate legacy `tags` → `serverDefinedClientTags`
413
+ if (!client.serverDefinedClientTags && client.tags) {
414
+ client.serverDefinedClientTags = client.tags;
415
+ delete client.tags;
416
+ await this.persistClient(client);
417
+ }
367
418
  this.clients.set(client.clientId, client);
368
419
  }
369
420
  }
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/dcrouter',
6
- version: '11.13.0',
6
+ version: '11.15.0',
7
7
  description: 'A multifaceted routing service handling mail and SMS delivery functions.'
8
8
  }