From a65c2ec0961b3f068f14e691bba9c122aca24cfc Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Thu, 19 Mar 2026 21:30:06 +0000 Subject: [PATCH] feat(remoteingress): add UDP listen port derivation and edge configuration support --- changelog.md | 7 +++ package.json | 2 +- pnpm-lock.yaml | 10 ++-- ts/00_commitinfo_data.ts | 2 +- .../classes.remoteingress-manager.ts | 51 ++++++++++++++++++- ts_interfaces/data/remoteingress.ts | 4 ++ ts_web/00_commitinfo_data.ts | 2 +- 7 files changed, 68 insertions(+), 10 deletions(-) diff --git a/changelog.md b/changelog.md index e258931..e66f1f5 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,12 @@ # Changelog +## 2026-03-19 - 11.8.0 - feat(remoteingress) +add UDP listen port derivation and edge configuration support + +- derive UDP ports from remote ingress routes using transport 'udp' or 'all' +- expose effective UDP listen ports in allowed edge payloads and remote ingress interfaces +- update @push.rocks/smartproxy to ^25.16.2 + ## 2026-03-19 - 11.7.1 - fix(deps) bump @push.rocks/smartproxy to ^25.16.0 diff --git a/package.json b/package.json index c945da5..def3cc5 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "@push.rocks/smartnetwork": "^4.4.0", "@push.rocks/smartpath": "^6.0.0", "@push.rocks/smartpromise": "^4.2.3", - "@push.rocks/smartproxy": "^25.16.0", + "@push.rocks/smartproxy": "^25.16.2", "@push.rocks/smartradius": "^1.1.1", "@push.rocks/smartrequest": "^5.0.1", "@push.rocks/smartrx": "^3.0.10", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 30fab57..3b722f4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -78,8 +78,8 @@ importers: specifier: ^4.2.3 version: 4.2.3 '@push.rocks/smartproxy': - specifier: ^25.16.0 - version: 25.16.0 + specifier: ^25.16.2 + version: 25.16.2 '@push.rocks/smartradius': specifier: ^1.1.1 version: 1.1.1 @@ -1256,8 +1256,8 @@ packages: '@push.rocks/smartpromise@4.2.3': resolution: {integrity: sha512-Ycg/TJR+tMt+S3wSFurOpEoW6nXv12QBtKXgBcjMZ4RsdO28geN46U09osPn9N9WuwQy1PkmTV5J/V4F9U8qEw==} - '@push.rocks/smartproxy@25.16.0': - resolution: {integrity: sha512-X3XtOjbYUHukKeZDBWO2g69k3otYI8JsA8RQdQySTuAKPnqj4QjhFJRfkgc+jGgWaWk3j5Mq5RMGsoVOmu4VGA==} + '@push.rocks/smartproxy@25.16.2': + resolution: {integrity: sha512-o42ipMKmgfl5dOVWF3lCLYrCxLTl+OGvwmtK8smqFG6BCfUvJrd38zDH2j3fA6bxKdIgr5rJFDSjJypznL+6Gg==} '@push.rocks/smartpuppeteer@2.0.5': resolution: {integrity: sha512-yK/qSeWVHIGWRp3c8S5tfdGP6WCKllZC4DR8d8CQlEjszOSBmHtlTdyyqOMBZ/BA4kd+eU5f3A1r4K2tGYty1g==} @@ -6539,7 +6539,7 @@ snapshots: '@push.rocks/smartpromise@4.2.3': {} - '@push.rocks/smartproxy@25.16.0': + '@push.rocks/smartproxy@25.16.2': dependencies: '@push.rocks/smartcrypto': 2.0.4 '@push.rocks/smartlog': 3.2.1 diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 9718e68..249361e 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@serve.zone/dcrouter', - version: '11.7.1', + version: '11.8.0', description: 'A multifaceted routing service handling mail and SMS delivery functions.' } diff --git a/ts/remoteingress/classes.remoteingress-manager.ts b/ts/remoteingress/classes.remoteingress-manager.ts index ccc335a..4e7f04f 100644 --- a/ts/remoteingress/classes.remoteingress-manager.ts +++ b/ts/remoteingress/classes.remoteingress-manager.ts @@ -94,6 +94,38 @@ export class RemoteIngressManager { return [...ports].sort((a, b) => a - b); } + /** + * Derive UDP listen ports for an edge from routes with transport 'udp' or 'all'. + * These ports need UDP listeners on the edge (e.g. for QUIC/HTTP3). + */ + public deriveUdpPortsForEdge(edgeId: string, edgeTags?: string[]): number[] { + const ports = new Set(); + + for (const route of this.routes) { + if (!route.remoteIngress?.enabled) continue; + + // Apply edge filter if present + const filter = route.remoteIngress.edgeFilter; + if (filter && filter.length > 0) { + const idMatch = filter.includes(edgeId); + const tagMatch = edgeTags?.some((tag) => filter.includes(tag)) ?? false; + if (!idMatch && !tagMatch) continue; + } + + // Only include ports from routes that listen on UDP + const transport = route.match?.transport; + if (transport === 'udp' || transport === 'all') { + if (route.match?.ports) { + for (const p of extractPorts(route.match.ports)) { + ports.add(p); + } + } + } + } + + return [...ports].sort((a, b) => a - b); + } + /** * Get the effective listen ports for an edge. * Manual ports are always included. Auto-derived ports are added (union) when autoDerivePorts is true. @@ -106,6 +138,18 @@ export class RemoteIngressManager { return [...new Set([...manualPorts, ...derivedPorts])].sort((a, b) => a - b); } + /** + * Get the effective UDP listen ports for an edge. + * Manual UDP ports are always included. Auto-derived UDP ports are added when autoDerivePorts is true. + */ + public getEffectiveListenPortsUdp(edge: IRemoteIngress): number[] { + const manualPorts = edge.listenPortsUdp || []; + const shouldDerive = edge.autoDerivePorts !== false; + if (!shouldDerive) return [...manualPorts].sort((a, b) => a - b); + const derivedPorts = this.deriveUdpPortsForEdge(edge.id, edge.tags); + return [...new Set([...manualPorts, ...derivedPorts])].sort((a, b) => a - b); + } + /** * Get manual and derived port breakdown for an edge (used in API responses). * Derived ports exclude any ports already present in the manual list. @@ -241,15 +285,18 @@ export class RemoteIngressManager { /** * Get the list of allowed edges (enabled only) for the Rust hub. + * Includes listenPortsUdp when routes with transport 'udp' or 'all' are present. */ - public getAllowedEdges(): Array<{ id: string; secret: string; listenPorts: number[] }> { - const result: Array<{ id: string; secret: string; listenPorts: number[] }> = []; + public getAllowedEdges(): Array<{ id: string; secret: string; listenPorts: number[]; listenPortsUdp?: number[] }> { + const result: Array<{ id: string; secret: string; listenPorts: number[]; listenPortsUdp?: number[] }> = []; for (const edge of this.edges.values()) { if (edge.enabled) { + const listenPortsUdp = this.getEffectiveListenPortsUdp(edge); result.push({ id: edge.id, secret: edge.secret, listenPorts: this.getEffectiveListenPorts(edge), + ...(listenPortsUdp.length > 0 ? { listenPortsUdp } : {}), }); } } diff --git a/ts_interfaces/data/remoteingress.ts b/ts_interfaces/data/remoteingress.ts index 15bd7eb..0f8e6f7 100644 --- a/ts_interfaces/data/remoteingress.ts +++ b/ts_interfaces/data/remoteingress.ts @@ -8,6 +8,8 @@ export interface IRemoteIngress { name: string; secret: string; listenPorts: number[]; + /** UDP listen ports (e.g. for QUIC/HTTP3). Derived from routes with transport 'udp' or 'all'. */ + listenPortsUdp?: number[]; enabled: boolean; /** Whether to auto-derive ports from remoteIngress-tagged routes. Defaults to true. */ autoDerivePorts: boolean; @@ -20,6 +22,8 @@ export interface IRemoteIngress { manualPorts?: number[]; /** Ports auto-derived from route configs — only present in API responses. */ derivedPorts?: number[]; + /** Effective UDP ports (union of manual + derived) — only present in API responses. */ + effectiveListenPortsUdp?: number[]; } /** diff --git a/ts_web/00_commitinfo_data.ts b/ts_web/00_commitinfo_data.ts index 9718e68..249361e 100644 --- a/ts_web/00_commitinfo_data.ts +++ b/ts_web/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@serve.zone/dcrouter', - version: '11.7.1', + version: '11.8.0', description: 'A multifaceted routing service handling mail and SMS delivery functions.' }