@serve.zone/remoteingress 3.2.0 → 3.3.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.
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/remoteingress',
6
- version: '3.2.0',
6
+ version: '3.3.0',
7
7
  description: 'Edge ingress tunnel for DcRouter - accepts incoming TCP connections at network edge and tunnels them to DcRouter SmartProxy preserving client IP via PROXY protocol v1.'
8
8
  };
9
9
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSwyQkFBMkI7SUFDakMsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLHlLQUF5SztDQUN2TCxDQUFBIn0=
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serve.zone/remoteingress",
3
- "version": "3.2.0",
3
+ "version": "3.3.0",
4
4
  "private": false,
5
5
  "description": "Edge ingress tunnel for DcRouter - accepts incoming TCP connections at network edge and tunnels them to DcRouter SmartProxy preserving client IP via PROXY protocol v1.",
6
6
  "main": "dist_ts/index.js",
@@ -9,7 +9,7 @@
9
9
  "author": "Task Venture Capital GmbH",
10
10
  "license": "MIT",
11
11
  "scripts": {
12
- "test": "(tstest test/ --web)",
12
+ "test": "(tstest test/ --verbose --logfile --timeout 60)",
13
13
  "build": "(tsbuild tsfolders --allowimplicitany && tsrust)",
14
14
  "buildDocs": "(tsdoc)"
15
15
  },
package/readme.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @serve.zone/remoteingress
2
2
 
3
- Edge ingress tunnel for DcRouter — accepts incoming TCP connections at the network edge and tunnels them to a DcRouter SmartProxy instance, preserving the original client IP via PROXY protocol v1.
3
+ Edge ingress tunnel for DcRouter — accepts incoming TCP connections at the network edge and tunnels them over a single encrypted TLS connection to a DcRouter SmartProxy instance, preserving the original client IP via PROXY protocol v1.
4
4
 
5
5
  ## Issue Reporting and Security
6
6
 
@@ -22,8 +22,8 @@ pnpm install @serve.zone/remoteingress
22
22
  │ │ (multiplexed frames + │ │
23
23
  │ RemoteIngressEdge │ shared-secret auth) │ RemoteIngressHub │
24
24
  │ Accepts client TCP │ │ Forwards to │
25
- │ connections │ │ SmartProxy on │
26
- │ │ local ports │
25
+ │ connections on │ │ SmartProxy on │
26
+ hub-assigned ports │ │ local ports │
27
27
  └─────────────────────┘ └─────────────────────┘
28
28
  ▲ │
29
29
  │ TCP from end users ▼
@@ -32,8 +32,8 @@ pnpm install @serve.zone/remoteingress
32
32
 
33
33
  | Component | Role |
34
34
  |-----------|------|
35
- | **RemoteIngressEdge** | Deployed at the network edge (e.g. a VPS or cloud instance). Accepts raw TCP connections and multiplexes them over a single TLS tunnel to the hub. |
36
- | **RemoteIngressHub** | Deployed alongside DcRouter/SmartProxy in a private cluster. Accepts edge connections, demuxes streams, and forwards each to SmartProxy with a PROXY protocol v1 header so the real client IP is preserved. |
35
+ | **RemoteIngressEdge** | Deployed at the network edge (e.g. a VPS or cloud instance). Listens on ports assigned by the hub, accepts raw TCP connections, and multiplexes them over a single TLS tunnel to the hub. Ports are hot-reloadable — the hub can change them at runtime. |
36
+ | **RemoteIngressHub** | Deployed alongside DcRouter/SmartProxy in a private cluster. Accepts edge connections, demuxes streams, and forwards each to SmartProxy with a PROXY protocol v1 header so the real client IP is preserved. Controls which ports each edge listens on. |
37
37
  | **Rust Binary** (`remoteingress-bin`) | The performance-critical networking core. Managed via `@push.rocks/smartrust` RustBridge IPC — you never interact with it directly. Cross-compiled for `linux/amd64` and `linux/arm64`. |
38
38
 
39
39
  ### ✨ Key Features
@@ -45,6 +45,7 @@ pnpm install @serve.zone/remoteingress
45
45
  - 🎫 **Connection tokens** — encode all connection details into a single opaque string
46
46
  - 📡 **STUN-based public IP discovery** — the edge automatically discovers its public IP via Cloudflare STUN
47
47
  - 🔄 **Auto-reconnect** with exponential backoff if the tunnel drops
48
+ - 🎛️ **Dynamic port configuration** — the hub assigns listen ports per edge and can hot-reload them at runtime via `FRAME_CONFIG` frames
48
49
  - 📣 **Event-driven** — both Hub and Edge extend `EventEmitter` for real-time monitoring
49
50
  - ⚡ **Rust core** — all frame encoding, TLS, and TCP proxying happen in native code for maximum throughput
50
51
 
@@ -79,10 +80,28 @@ await hub.start({
79
80
  targetHost: '127.0.0.1', // SmartProxy host to forward streams to (default: 127.0.0.1)
80
81
  });
81
82
 
82
- // Register which edges are allowed to connect
83
+ // Register which edges are allowed to connect, including their listen ports
83
84
  await hub.updateAllowedEdges([
84
- { id: 'edge-nyc-01', secret: 'supersecrettoken1' },
85
- { id: 'edge-fra-02', secret: 'supersecrettoken2' },
85
+ {
86
+ id: 'edge-nyc-01',
87
+ secret: 'supersecrettoken1',
88
+ listenPorts: [80, 443], // ports the edge should listen on
89
+ stunIntervalSecs: 300, // STUN discovery interval (default: 300)
90
+ },
91
+ {
92
+ id: 'edge-fra-02',
93
+ secret: 'supersecrettoken2',
94
+ listenPorts: [443, 8080],
95
+ },
96
+ ]);
97
+
98
+ // Dynamically update ports for a connected edge — changes are pushed instantly
99
+ await hub.updateAllowedEdges([
100
+ {
101
+ id: 'edge-nyc-01',
102
+ secret: 'supersecrettoken1',
103
+ listenPorts: [80, 443, 8443], // added port 8443 — edge picks it up in real time
104
+ },
86
105
  ]);
87
106
 
88
107
  // Check status at any time
@@ -116,6 +135,8 @@ const edge = new RemoteIngressEdge();
116
135
  edge.on('tunnelConnected', () => console.log('Tunnel established'));
117
136
  edge.on('tunnelDisconnected', () => console.log('Tunnel lost — will auto-reconnect'));
118
137
  edge.on('publicIpDiscovered', ({ ip }) => console.log(`Public IP: ${ip}`));
138
+ edge.on('portsAssigned', ({ listenPorts }) => console.log(`Listening on ports: ${listenPorts}`));
139
+ edge.on('portsUpdated', ({ listenPorts }) => console.log(`Ports updated: ${listenPorts}`));
119
140
 
120
141
  // Single token contains hubHost, hubPort, edgeId, and secret
121
142
  await edge.start({
@@ -133,6 +154,8 @@ const edge = new RemoteIngressEdge();
133
154
  edge.on('tunnelConnected', () => console.log('Tunnel established'));
134
155
  edge.on('tunnelDisconnected', () => console.log('Tunnel lost — will auto-reconnect'));
135
156
  edge.on('publicIpDiscovered', ({ ip }) => console.log(`Public IP: ${ip}`));
157
+ edge.on('portsAssigned', ({ listenPorts }) => console.log(`Listening on ports: ${listenPorts}`));
158
+ edge.on('portsUpdated', ({ listenPorts }) => console.log(`Ports updated: ${listenPorts}`));
136
159
 
137
160
  await edge.start({
138
161
  hubHost: 'hub.example.com', // hostname or IP of the hub
@@ -194,7 +217,7 @@ Tokens are base64url-encoded (URL-safe, no padding) — safe to pass as environm
194
217
  |-------------------|-------------|
195
218
  | `start(config?)` | Spawns the Rust binary and starts the tunnel listener. Config: `{ tunnelPort?: number, targetHost?: string }` |
196
219
  | `stop()` | Gracefully shuts down the hub and kills the Rust process. |
197
- | `updateAllowedEdges(edges)` | Dynamically update which edges are authorized. Each edge: `{ id: string, secret: string }` |
220
+ | `updateAllowedEdges(edges)` | Dynamically update which edges are authorized and what ports they listen on. Each edge: `{ id: string, secret: string, listenPorts?: number[], stunIntervalSecs?: number }`. If ports change for a connected edge, the update is pushed immediately via a `FRAME_CONFIG` frame. |
198
221
  | `getStatus()` | Returns current hub status including connected edges and active stream counts. |
199
222
  | `running` | `boolean` — whether the Rust binary is alive. |
200
223
 
@@ -204,12 +227,12 @@ Tokens are base64url-encoded (URL-safe, no padding) — safe to pass as environm
204
227
 
205
228
  | Method / Property | Description |
206
229
  |-------------------|-------------|
207
- | `start(config)` | Spawns the Rust binary and connects to the hub. Accepts `{ token: string }` or `IEdgeConfig`. |
230
+ | `start(config)` | Spawns the Rust binary and connects to the hub. Accepts `{ token: string }` or `IEdgeConfig`. Listen ports are received from the hub during handshake. |
208
231
  | `stop()` | Gracefully shuts down the edge and kills the Rust process. |
209
- | `getStatus()` | Returns current edge status including connection state, public IP, and active streams. |
232
+ | `getStatus()` | Returns current edge status including connection state, public IP, listen ports, and active streams. |
210
233
  | `running` | `boolean` — whether the Rust binary is alive. |
211
234
 
212
- **Events:** `tunnelConnected`, `tunnelDisconnected`, `publicIpDiscovered`
235
+ **Events:** `tunnelConnected`, `tunnelDisconnected`, `publicIpDiscovered`, `portsAssigned`, `portsUpdated`
213
236
 
214
237
  ### Token Utilities
215
238
 
@@ -246,7 +269,7 @@ interface IConnectionTokenData {
246
269
  The tunnel uses a custom binary frame protocol over TLS:
247
270
 
248
271
  ```
249
- [stream_id: 4 bytes][type: 1 byte][length: 4 bytes][payload: N bytes]
272
+ [stream_id: 4 bytes BE][type: 1 byte][length: 4 bytes BE][payload: N bytes]
250
273
  ```
251
274
 
252
275
  | Frame Type | Value | Direction | Purpose |
@@ -256,8 +279,18 @@ The tunnel uses a custom binary frame protocol over TLS:
256
279
  | `CLOSE` | `0x03` | Edge → Hub | Client closed the connection |
257
280
  | `DATA_BACK` | `0x04` | Hub → Edge | Response data flowing downstream |
258
281
  | `CLOSE_BACK` | `0x05` | Hub → Edge | Upstream (SmartProxy) closed the connection |
282
+ | `CONFIG` | `0x06` | Hub → Edge | Runtime configuration update (e.g. port changes); payload is JSON |
283
+
284
+ Max payload size per frame: **16 MB**. Stream IDs are 32-bit unsigned integers.
259
285
 
260
- Max payload size per frame: **16 MB**.
286
+ ### Handshake Sequence
287
+
288
+ 1. Edge opens a TLS connection to the hub
289
+ 2. Edge sends: `EDGE <edgeId> <secret>\n`
290
+ 3. Hub verifies credentials (constant-time comparison) and responds with JSON: `{"listenPorts":[...],"stunIntervalSecs":300}\n`
291
+ 4. Edge starts TCP listeners on the assigned ports
292
+ 5. Frame protocol begins — `OPEN`/`DATA`/`CLOSE` frames flow in both directions
293
+ 6. Hub can push `CONFIG` frames at any time to update the edge's listen ports
261
294
 
262
295
  ## 💡 Example Scenarios
263
296
 
@@ -292,6 +325,22 @@ const edge = new RemoteIngressEdge();
292
325
  await edge.start({ token });
293
326
  ```
294
327
 
328
+ ### 5. Dynamic Port Management
329
+
330
+ The hub controls which ports each edge listens on. Ports can be changed at runtime without restarting the edge — the hub pushes a `CONFIG` frame and the edge hot-reloads its TCP listeners.
331
+
332
+ ```typescript
333
+ // Initially assign ports 80 and 443
334
+ await hub.updateAllowedEdges([
335
+ { id: 'edge-nyc-01', secret: 'secret', listenPorts: [80, 443] },
336
+ ]);
337
+
338
+ // Later, add port 8080 — the connected edge picks it up instantly
339
+ await hub.updateAllowedEdges([
340
+ { id: 'edge-nyc-01', secret: 'secret', listenPorts: [80, 443, 8080] },
341
+ ]);
342
+ ```
343
+
295
344
  ## License and Legal Information
296
345
 
297
346
  This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./LICENSE) file.
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/remoteingress',
6
- version: '3.2.0',
6
+ version: '3.3.0',
7
7
  description: 'Edge ingress tunnel for DcRouter - accepts incoming TCP connections at network edge and tunnels them to DcRouter SmartProxy preserving client IP via PROXY protocol v1.'
8
8
  }