@serve.zone/remoteingress 4.12.0 → 4.13.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.
|
Binary file
|
|
Binary file
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@serve.zone/remoteingress',
|
|
6
|
-
version: '4.
|
|
7
|
-
description: 'Edge ingress tunnel for DcRouter -
|
|
6
|
+
version: '4.13.0',
|
|
7
|
+
description: 'Edge ingress tunnel for DcRouter - tunnels TCP and UDP traffic from the network edge to SmartProxy over TLS or QUIC, preserving client IP via PROXY protocol.'
|
|
8
8
|
};
|
|
9
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
9
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSwyQkFBMkI7SUFDakMsT0FBTyxFQUFFLFFBQVE7SUFDakIsV0FBVyxFQUFFLCtKQUErSjtDQUM3SyxDQUFBIn0=
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@serve.zone/remoteingress",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.13.0",
|
|
4
4
|
"private": false,
|
|
5
|
-
"description": "Edge ingress tunnel for DcRouter -
|
|
5
|
+
"description": "Edge ingress tunnel for DcRouter - tunnels TCP and UDP traffic from the network edge to SmartProxy over TLS or QUIC, preserving client IP via PROXY protocol.",
|
|
6
6
|
"main": "dist_ts/index.js",
|
|
7
7
|
"typings": "dist_ts/index.d.ts",
|
|
8
8
|
"type": "module",
|
package/readme.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @serve.zone/remoteingress
|
|
2
2
|
|
|
3
|
-
Edge ingress tunnel for DcRouter —
|
|
3
|
+
Edge ingress tunnel for DcRouter — tunnels **TCP and UDP** traffic from the network edge to a private DcRouter/SmartProxy cluster over encrypted TLS or QUIC connections, preserving the original client IP via PROXY protocol.
|
|
4
4
|
|
|
5
5
|
## Issue Reporting and Security
|
|
6
6
|
|
|
@@ -17,43 +17,46 @@ pnpm install @serve.zone/remoteingress
|
|
|
17
17
|
`@serve.zone/remoteingress` uses a **Hub/Edge** topology with a high-performance Rust core and a TypeScript API surface:
|
|
18
18
|
|
|
19
19
|
```
|
|
20
|
-
┌─────────────────────┐
|
|
20
|
+
┌─────────────────────┐ TLS or QUIC Tunnel ┌─────────────────────┐
|
|
21
21
|
│ Network Edge │ ◄══════════════════════════► │ Private Cluster │
|
|
22
|
-
│ │
|
|
23
|
-
│ RemoteIngressEdge │
|
|
24
|
-
│
|
|
25
|
-
│
|
|
26
|
-
│ hub-assigned
|
|
22
|
+
│ │ TCP+TLS: frame mux │ │
|
|
23
|
+
│ RemoteIngressEdge │ QUIC: native streams │ RemoteIngressHub │
|
|
24
|
+
│ │ UDP: QUIC datagrams │ │
|
|
25
|
+
│ Accepts TCP & UDP │ │ Forwards to │
|
|
26
|
+
│ on hub-assigned │ │ SmartProxy on │
|
|
27
|
+
│ ports │ │ local ports │
|
|
27
28
|
└─────────────────────┘ └─────────────────────┘
|
|
28
29
|
▲ │
|
|
29
|
-
│ TCP from end users
|
|
30
|
+
│ TCP + UDP from end users ▼
|
|
30
31
|
Internet DcRouter / SmartProxy
|
|
31
32
|
```
|
|
32
33
|
|
|
33
34
|
| Component | Role |
|
|
34
35
|
|-----------|------|
|
|
35
|
-
| **RemoteIngressEdge** | Deployed at the network edge (
|
|
36
|
-
| **RemoteIngressHub** | Deployed alongside DcRouter/SmartProxy in a private cluster. Accepts edge connections, demuxes streams, and forwards each to SmartProxy with
|
|
36
|
+
| **RemoteIngressEdge** | Deployed at the network edge (VPS, cloud instance). Listens on TCP and UDP ports assigned by the hub, accepts connections/datagrams, and tunnels them to the hub. Ports are hot-reloadable at runtime. |
|
|
37
|
+
| **RemoteIngressHub** | Deployed alongside DcRouter/SmartProxy in a private cluster. Accepts edge connections, demuxes streams/datagrams, and forwards each to SmartProxy with PROXY protocol headers so the real client IP is preserved. |
|
|
37
38
|
| **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
39
|
|
|
39
40
|
### ✨ Key Features
|
|
40
41
|
|
|
41
|
-
- 🔒 **
|
|
42
|
-
-
|
|
43
|
-
-
|
|
42
|
+
- 🔒 **Dual transport** — choose between TCP+TLS (frame-multiplexed) or QUIC (native stream multiplexing, zero head-of-line blocking)
|
|
43
|
+
- 🌐 **TCP + UDP tunneling** — tunnel any TCP connection or UDP datagram through the same edge/hub pair
|
|
44
|
+
- 📋 **PROXY protocol v1 & v2** — SmartProxy sees the real client IP for both TCP (v1 text) and UDP (v2 binary)
|
|
45
|
+
- 🔀 **Multiplexed streams** — thousands of concurrent TCP connections over a single tunnel
|
|
46
|
+
- ⚡ **QUIC datagrams** — UDP traffic forwarded via QUIC unreliable datagrams for lowest possible latency
|
|
44
47
|
- 🔑 **Shared-secret authentication** — edges must present valid credentials to connect
|
|
45
|
-
- 🎫 **Connection tokens** — encode all connection details into a single opaque string
|
|
46
|
-
- 📡 **STUN-based public IP discovery** —
|
|
48
|
+
- 🎫 **Connection tokens** — encode all connection details into a single opaque base64url string
|
|
49
|
+
- 📡 **STUN-based public IP discovery** — edges automatically discover their public IP via Cloudflare STUN
|
|
47
50
|
- 🔄 **Auto-reconnect** with exponential backoff if the tunnel drops
|
|
48
|
-
- 🎛️ **Dynamic port configuration** — the hub assigns listen ports per edge
|
|
51
|
+
- 🎛️ **Dynamic port configuration** — the hub assigns TCP and UDP listen ports per edge, hot-reloadable at runtime
|
|
49
52
|
- 📣 **Event-driven** — both Hub and Edge extend `EventEmitter` for real-time monitoring
|
|
50
|
-
- ⚡ **Rust core** — all frame encoding, TLS, and TCP proxying happen in native code for maximum throughput
|
|
51
53
|
- 🎚️ **3-tier QoS** — control frames, normal data, and sustained (elephant flow) traffic each get their own priority queue
|
|
52
54
|
- 📊 **Adaptive flow control** — per-stream windows scale with active stream count to prevent memory overuse
|
|
55
|
+
- 🕒 **UDP session management** — automatic session tracking with 60s idle timeout and cleanup
|
|
53
56
|
|
|
54
57
|
## 🚀 Usage
|
|
55
58
|
|
|
56
|
-
Both classes are imported from the package and communicate with the Rust binary under the hood.
|
|
59
|
+
Both classes are imported from the package and communicate with the Rust binary under the hood.
|
|
57
60
|
|
|
58
61
|
### Setting Up the Hub (Private Cluster Side)
|
|
59
62
|
|
|
@@ -63,32 +66,25 @@ import { RemoteIngressHub } from '@serve.zone/remoteingress';
|
|
|
63
66
|
const hub = new RemoteIngressHub();
|
|
64
67
|
|
|
65
68
|
// Listen for events
|
|
66
|
-
hub.on('edgeConnected', ({ edgeId }) => {
|
|
67
|
-
|
|
68
|
-
});
|
|
69
|
-
hub.on('
|
|
70
|
-
console.log(`Edge ${edgeId} disconnected`);
|
|
71
|
-
});
|
|
72
|
-
hub.on('streamOpened', ({ edgeId, streamId }) => {
|
|
73
|
-
console.log(`Stream ${streamId} opened from edge ${edgeId}`);
|
|
74
|
-
});
|
|
75
|
-
hub.on('streamClosed', ({ edgeId, streamId }) => {
|
|
76
|
-
console.log(`Stream ${streamId} closed from edge ${edgeId}`);
|
|
77
|
-
});
|
|
69
|
+
hub.on('edgeConnected', ({ edgeId }) => console.log(`Edge ${edgeId} connected`));
|
|
70
|
+
hub.on('edgeDisconnected', ({ edgeId }) => console.log(`Edge ${edgeId} disconnected`));
|
|
71
|
+
hub.on('streamOpened', ({ edgeId, streamId }) => console.log(`Stream ${streamId} from ${edgeId}`));
|
|
72
|
+
hub.on('streamClosed', ({ edgeId, streamId }) => console.log(`Stream ${streamId} closed`));
|
|
78
73
|
|
|
79
|
-
// Start the hub —
|
|
74
|
+
// Start the hub — listens for edge connections on both TCP and QUIC (same port)
|
|
80
75
|
await hub.start({
|
|
81
76
|
tunnelPort: 8443, // port edges connect to (default: 8443)
|
|
82
|
-
targetHost: '127.0.0.1', // SmartProxy host to forward
|
|
77
|
+
targetHost: '127.0.0.1', // SmartProxy host to forward traffic to
|
|
83
78
|
});
|
|
84
79
|
|
|
85
|
-
// Register
|
|
80
|
+
// Register allowed edges with TCP and UDP listen ports
|
|
86
81
|
await hub.updateAllowedEdges([
|
|
87
82
|
{
|
|
88
83
|
id: 'edge-nyc-01',
|
|
89
84
|
secret: 'supersecrettoken1',
|
|
90
|
-
listenPorts: [80, 443], // ports the edge should listen on
|
|
91
|
-
|
|
85
|
+
listenPorts: [80, 443], // TCP ports the edge should listen on
|
|
86
|
+
listenPortsUdp: [53, 51820], // UDP ports (e.g., DNS, WireGuard)
|
|
87
|
+
stunIntervalSecs: 300,
|
|
92
88
|
},
|
|
93
89
|
{
|
|
94
90
|
id: 'edge-fra-02',
|
|
@@ -97,38 +93,29 @@ await hub.updateAllowedEdges([
|
|
|
97
93
|
},
|
|
98
94
|
]);
|
|
99
95
|
|
|
100
|
-
// Dynamically update ports
|
|
96
|
+
// Dynamically update ports — changes are pushed instantly to connected edges
|
|
101
97
|
await hub.updateAllowedEdges([
|
|
102
98
|
{
|
|
103
99
|
id: 'edge-nyc-01',
|
|
104
100
|
secret: 'supersecrettoken1',
|
|
105
|
-
listenPorts: [80, 443, 8443],
|
|
101
|
+
listenPorts: [80, 443, 8443], // added TCP port 8443
|
|
102
|
+
listenPortsUdp: [53], // removed WireGuard UDP port
|
|
106
103
|
},
|
|
107
104
|
]);
|
|
108
105
|
|
|
109
|
-
// Check status
|
|
106
|
+
// Check status
|
|
110
107
|
const status = await hub.getStatus();
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
// running: true,
|
|
114
|
-
// tunnelPort: 8443,
|
|
115
|
-
// connectedEdges: [
|
|
116
|
-
// { edgeId: 'edge-nyc-01', connectedAt: 1700000000, activeStreams: 12 }
|
|
117
|
-
// ]
|
|
118
|
-
// }
|
|
119
|
-
|
|
120
|
-
// Graceful shutdown
|
|
108
|
+
// { running: true, tunnelPort: 8443, connectedEdges: [...] }
|
|
109
|
+
|
|
121
110
|
await hub.stop();
|
|
122
111
|
```
|
|
123
112
|
|
|
124
113
|
### Setting Up the Edge (Network Edge Side)
|
|
125
114
|
|
|
126
|
-
The edge can
|
|
115
|
+
The edge can connect via **TCP+TLS** (default) or **QUIC** transport.
|
|
127
116
|
|
|
128
117
|
#### Option A: Connection Token (Recommended)
|
|
129
118
|
|
|
130
|
-
A single token encodes all connection details — ideal for provisioning edges at scale:
|
|
131
|
-
|
|
132
119
|
```typescript
|
|
133
120
|
import { RemoteIngressEdge } from '@serve.zone/remoteingress';
|
|
134
121
|
|
|
@@ -137,79 +124,64 @@ const edge = new RemoteIngressEdge();
|
|
|
137
124
|
edge.on('tunnelConnected', () => console.log('Tunnel established'));
|
|
138
125
|
edge.on('tunnelDisconnected', () => console.log('Tunnel lost — will auto-reconnect'));
|
|
139
126
|
edge.on('publicIpDiscovered', ({ ip }) => console.log(`Public IP: ${ip}`));
|
|
140
|
-
edge.on('portsAssigned', ({ listenPorts }) => console.log(`
|
|
141
|
-
edge.on('portsUpdated', ({ listenPorts }) => console.log(`Ports updated: ${listenPorts}`));
|
|
127
|
+
edge.on('portsAssigned', ({ listenPorts }) => console.log(`TCP ports: ${listenPorts}`));
|
|
142
128
|
|
|
143
|
-
// Single token contains hubHost, hubPort, edgeId, and secret
|
|
144
129
|
await edge.start({
|
|
145
|
-
token: '
|
|
130
|
+
token: 'eyJoIjoiaHViLmV4YW1wbGUuY29tIiwi...',
|
|
146
131
|
});
|
|
147
132
|
```
|
|
148
133
|
|
|
149
|
-
#### Option B: Explicit Config
|
|
134
|
+
#### Option B: Explicit Config with QUIC Transport
|
|
150
135
|
|
|
151
136
|
```typescript
|
|
152
137
|
import { RemoteIngressEdge } from '@serve.zone/remoteingress';
|
|
153
138
|
|
|
154
139
|
const edge = new RemoteIngressEdge();
|
|
155
140
|
|
|
156
|
-
edge.on('tunnelConnected', () => console.log('Tunnel established'));
|
|
157
|
-
edge.on('tunnelDisconnected', () => console.log('Tunnel lost — will auto-reconnect'));
|
|
158
|
-
edge.on('publicIpDiscovered', ({ ip }) => console.log(`Public IP: ${ip}`));
|
|
159
|
-
edge.on('portsAssigned', ({ listenPorts }) => console.log(`Listening on ports: ${listenPorts}`));
|
|
160
|
-
edge.on('portsUpdated', ({ listenPorts }) => console.log(`Ports updated: ${listenPorts}`));
|
|
161
|
-
|
|
162
141
|
await edge.start({
|
|
163
|
-
hubHost: 'hub.example.com',
|
|
164
|
-
hubPort: 8443,
|
|
165
|
-
edgeId: 'edge-nyc-01',
|
|
166
|
-
secret: 'supersecrettoken1',
|
|
142
|
+
hubHost: 'hub.example.com',
|
|
143
|
+
hubPort: 8443,
|
|
144
|
+
edgeId: 'edge-nyc-01',
|
|
145
|
+
secret: 'supersecrettoken1',
|
|
146
|
+
transportMode: 'quic', // 'tcpTls' (default) | 'quic' | 'quicWithFallback'
|
|
167
147
|
});
|
|
168
148
|
|
|
169
|
-
// Check status at any time
|
|
170
149
|
const edgeStatus = await edge.getStatus();
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
// running: true,
|
|
174
|
-
// connected: true,
|
|
175
|
-
// publicIp: '203.0.113.42',
|
|
176
|
-
// activeStreams: 5,
|
|
177
|
-
// listenPorts: [80, 443]
|
|
178
|
-
// }
|
|
179
|
-
|
|
180
|
-
// Graceful shutdown
|
|
150
|
+
// { running: true, connected: true, publicIp: '203.0.113.42', activeStreams: 5, listenPorts: [80, 443] }
|
|
151
|
+
|
|
181
152
|
await edge.stop();
|
|
182
153
|
```
|
|
183
154
|
|
|
155
|
+
#### Transport Modes
|
|
156
|
+
|
|
157
|
+
| Mode | Description |
|
|
158
|
+
|------|-------------|
|
|
159
|
+
| `'tcpTls'` | **Default.** Single TLS connection with frame-based multiplexing. Universal compatibility. |
|
|
160
|
+
| `'quic'` | QUIC with native stream multiplexing. Eliminates head-of-line blocking. Uses QUIC datagrams for UDP traffic. |
|
|
161
|
+
| `'quicWithFallback'` | Tries QUIC first (5s timeout), falls back to TCP+TLS if UDP is blocked by the network. |
|
|
162
|
+
|
|
184
163
|
### 🎫 Connection Tokens
|
|
185
164
|
|
|
186
|
-
|
|
165
|
+
Encode all connection details into a single opaque string for easy distribution:
|
|
187
166
|
|
|
188
167
|
```typescript
|
|
189
168
|
import { encodeConnectionToken, decodeConnectionToken } from '@serve.zone/remoteingress';
|
|
190
169
|
|
|
191
|
-
// Hub
|
|
170
|
+
// Hub operator generates a token
|
|
192
171
|
const token = encodeConnectionToken({
|
|
193
172
|
hubHost: 'hub.example.com',
|
|
194
173
|
hubPort: 8443,
|
|
195
174
|
edgeId: 'edge-nyc-01',
|
|
196
175
|
secret: 'supersecrettoken1',
|
|
197
176
|
});
|
|
198
|
-
console.log(token);
|
|
199
177
|
// => 'eyJoIjoiaHViLmV4YW1wbGUuY29tIiwi...'
|
|
200
178
|
|
|
201
|
-
// Edge
|
|
179
|
+
// Edge operator decodes (optional — start() does this automatically)
|
|
202
180
|
const data = decodeConnectionToken(token);
|
|
203
|
-
|
|
204
|
-
// {
|
|
205
|
-
// hubHost: 'hub.example.com',
|
|
206
|
-
// hubPort: 8443,
|
|
207
|
-
// edgeId: 'edge-nyc-01',
|
|
208
|
-
// secret: 'supersecrettoken1'
|
|
209
|
-
// }
|
|
181
|
+
// { hubHost: 'hub.example.com', hubPort: 8443, edgeId: 'edge-nyc-01', secret: '...' }
|
|
210
182
|
```
|
|
211
183
|
|
|
212
|
-
Tokens are base64url-encoded
|
|
184
|
+
Tokens are base64url-encoded — safe for environment variables, CLI arguments, and config files.
|
|
213
185
|
|
|
214
186
|
## 📖 API Reference
|
|
215
187
|
|
|
@@ -217,10 +189,10 @@ Tokens are base64url-encoded (URL-safe, no padding) — safe to pass as environm
|
|
|
217
189
|
|
|
218
190
|
| Method / Property | Description |
|
|
219
191
|
|-------------------|-------------|
|
|
220
|
-
| `start(config?)` |
|
|
221
|
-
| `stop()` |
|
|
222
|
-
| `updateAllowedEdges(edges)` |
|
|
223
|
-
| `getStatus()` | Returns
|
|
192
|
+
| `start(config?)` | Start the hub. Config: `{ tunnelPort?: number, targetHost?: string }`. Listens on both TCP and UDP (QUIC) on the tunnel port. |
|
|
193
|
+
| `stop()` | Graceful shutdown. |
|
|
194
|
+
| `updateAllowedEdges(edges)` | Set authorized edges. Each: `{ id, secret, listenPorts?, listenPortsUdp?, stunIntervalSecs? }`. Port changes are pushed to connected edges in real time. |
|
|
195
|
+
| `getStatus()` | Returns `{ running, tunnelPort, connectedEdges: [...] }`. |
|
|
224
196
|
| `running` | `boolean` — whether the Rust binary is alive. |
|
|
225
197
|
|
|
226
198
|
**Events:** `edgeConnected`, `edgeDisconnected`, `streamOpened`, `streamClosed`
|
|
@@ -229,9 +201,9 @@ Tokens are base64url-encoded (URL-safe, no padding) — safe to pass as environm
|
|
|
229
201
|
|
|
230
202
|
| Method / Property | Description |
|
|
231
203
|
|-------------------|-------------|
|
|
232
|
-
| `start(config)` |
|
|
233
|
-
| `stop()` |
|
|
234
|
-
| `getStatus()` | Returns
|
|
204
|
+
| `start(config)` | Connect to hub. Accepts `{ token }` or `{ hubHost, hubPort, edgeId, secret, transportMode? }`. |
|
|
205
|
+
| `stop()` | Graceful shutdown. |
|
|
206
|
+
| `getStatus()` | Returns `{ running, connected, publicIp, activeStreams, listenPorts }`. |
|
|
235
207
|
| `running` | `boolean` — whether the Rust binary is alive. |
|
|
236
208
|
|
|
237
209
|
**Events:** `tunnelConnected`, `tunnelDisconnected`, `publicIpDiscovered`, `portsAssigned`, `portsUpdated`
|
|
@@ -240,8 +212,8 @@ Tokens are base64url-encoded (URL-safe, no padding) — safe to pass as environm
|
|
|
240
212
|
|
|
241
213
|
| Function | Description |
|
|
242
214
|
|----------|-------------|
|
|
243
|
-
| `encodeConnectionToken(data)` | Encodes
|
|
244
|
-
| `decodeConnectionToken(token)` | Decodes a token
|
|
215
|
+
| `encodeConnectionToken(data)` | Encodes connection info into a base64url token. |
|
|
216
|
+
| `decodeConnectionToken(token)` | Decodes a token. Throws on malformed input. |
|
|
245
217
|
|
|
246
218
|
### Interfaces
|
|
247
219
|
|
|
@@ -256,6 +228,8 @@ interface IEdgeConfig {
|
|
|
256
228
|
hubPort?: number; // default: 8443
|
|
257
229
|
edgeId: string;
|
|
258
230
|
secret: string;
|
|
231
|
+
bindAddress?: string;
|
|
232
|
+
transportMode?: 'tcpTls' | 'quic' | 'quicWithFallback';
|
|
259
233
|
}
|
|
260
234
|
|
|
261
235
|
interface IConnectionTokenData {
|
|
@@ -268,7 +242,9 @@ interface IConnectionTokenData {
|
|
|
268
242
|
|
|
269
243
|
## 🔌 Wire Protocol
|
|
270
244
|
|
|
271
|
-
|
|
245
|
+
### TCP+TLS Transport (Frame Protocol)
|
|
246
|
+
|
|
247
|
+
The tunnel uses a custom binary frame protocol over a single TLS connection:
|
|
272
248
|
|
|
273
249
|
```
|
|
274
250
|
[stream_id: 4 bytes BE][type: 1 byte][length: 4 bytes BE][payload: N bytes]
|
|
@@ -276,113 +252,124 @@ The tunnel uses a custom binary frame protocol over TLS:
|
|
|
276
252
|
|
|
277
253
|
| Frame Type | Value | Direction | Purpose |
|
|
278
254
|
|------------|-------|-----------|---------|
|
|
279
|
-
| `OPEN` | `0x01` | Edge → Hub | Open
|
|
280
|
-
| `DATA` | `0x02` | Edge → Hub | Client data
|
|
281
|
-
| `CLOSE` | `0x03` | Edge → Hub | Client closed
|
|
282
|
-
| `DATA_BACK` | `0x04` | Hub → Edge | Response data
|
|
283
|
-
| `CLOSE_BACK` | `0x05` | Hub → Edge | Upstream
|
|
284
|
-
| `CONFIG` | `0x06` | Hub → Edge | Runtime
|
|
285
|
-
| `PING` | `0x07` | Hub → Edge | Heartbeat probe (
|
|
255
|
+
| `OPEN` | `0x01` | Edge → Hub | Open TCP stream; payload is PROXY v1 header |
|
|
256
|
+
| `DATA` | `0x02` | Edge → Hub | Client data (upload) |
|
|
257
|
+
| `CLOSE` | `0x03` | Edge → Hub | Client closed connection |
|
|
258
|
+
| `DATA_BACK` | `0x04` | Hub → Edge | Response data (download) |
|
|
259
|
+
| `CLOSE_BACK` | `0x05` | Hub → Edge | Upstream closed connection |
|
|
260
|
+
| `CONFIG` | `0x06` | Hub → Edge | Runtime config update (JSON payload) |
|
|
261
|
+
| `PING` | `0x07` | Hub → Edge | Heartbeat probe (every 15s) |
|
|
286
262
|
| `PONG` | `0x08` | Edge → Hub | Heartbeat response |
|
|
287
|
-
| `WINDOW_UPDATE` | `0x09` | Edge → Hub |
|
|
288
|
-
| `WINDOW_UPDATE_BACK` | `0x0A` | Hub → Edge |
|
|
263
|
+
| `WINDOW_UPDATE` | `0x09` | Edge → Hub | Flow control: edge consumed N bytes |
|
|
264
|
+
| `WINDOW_UPDATE_BACK` | `0x0A` | Hub → Edge | Flow control: hub consumed N bytes |
|
|
265
|
+
| `UDP_OPEN` | `0x0B` | Edge → Hub | Open UDP session; payload is PROXY v2 header |
|
|
266
|
+
| `UDP_DATA` | `0x0C` | Edge → Hub | UDP datagram (upload) |
|
|
267
|
+
| `UDP_DATA_BACK` | `0x0D` | Hub → Edge | UDP datagram (download) |
|
|
268
|
+
| `UDP_CLOSE` | `0x0E` | Either | Close UDP session |
|
|
269
|
+
|
|
270
|
+
### QUIC Transport
|
|
289
271
|
|
|
290
|
-
|
|
272
|
+
When using QUIC, the frame protocol is replaced by native QUIC primitives:
|
|
273
|
+
|
|
274
|
+
- **TCP connections:** Each tunneled TCP connection gets its own QUIC bidirectional stream. No framing overhead.
|
|
275
|
+
- **UDP datagrams:** Forwarded via QUIC unreliable datagrams (RFC 9221). Format: `[session_id: 4 bytes][payload]`. Session open uses magic byte `0xFF`: `[session_id: 4][0xFF][PROXY v2 header]`.
|
|
276
|
+
- **Control channel:** First QUIC bidirectional stream carries auth handshake + config updates using `[type: 1][length: 4][payload]` format.
|
|
291
277
|
|
|
292
278
|
### Handshake Sequence
|
|
293
279
|
|
|
294
|
-
1. Edge opens a TLS connection to the hub
|
|
280
|
+
1. Edge opens a TLS or QUIC connection to the hub
|
|
295
281
|
2. Edge sends: `EDGE <edgeId> <secret>\n`
|
|
296
|
-
3. Hub verifies credentials (constant-time comparison) and responds with JSON:
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
282
|
+
3. Hub verifies credentials (constant-time comparison) and responds with JSON:
|
|
283
|
+
`{"listenPorts":[...],"listenPortsUdp":[...],"stunIntervalSecs":300}\n`
|
|
284
|
+
4. Edge starts TCP and UDP listeners on the assigned ports
|
|
285
|
+
5. Data flows — TCP frames/QUIC streams for TCP traffic, UDP frames/QUIC datagrams for UDP traffic
|
|
300
286
|
|
|
301
287
|
## 🎚️ QoS & Flow Control
|
|
302
288
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
### Priority Tiers
|
|
306
|
-
|
|
307
|
-
All outbound frames are queued into one of three priority levels:
|
|
289
|
+
### Priority Tiers (TCP+TLS Transport)
|
|
308
290
|
|
|
309
|
-
| Tier |
|
|
310
|
-
|
|
311
|
-
| 🔴 **Control**
|
|
312
|
-
| 🟡 **Data**
|
|
313
|
-
| 🟢 **Sustained**
|
|
314
|
-
|
|
315
|
-
This prevents large bulk transfers (e.g. git clones, file downloads) from starving interactive traffic and ensures `WINDOW_UPDATE` frames are never delayed — which would cause flow control deadlocks.
|
|
291
|
+
| Tier | Frames | Behavior |
|
|
292
|
+
|------|--------|----------|
|
|
293
|
+
| 🔴 **Control** | PING, PONG, WINDOW_UPDATE, OPEN, CLOSE, CONFIG | Always drained first. Never delayed. |
|
|
294
|
+
| 🟡 **Data** | DATA/DATA_BACK from normal streams, UDP frames | Drained when control queue is empty. |
|
|
295
|
+
| 🟢 **Sustained** | DATA/DATA_BACK from elephant flows | Lowest priority with guaranteed **1 MB/s** drain rate. |
|
|
316
296
|
|
|
317
297
|
### Sustained Stream Classification
|
|
318
298
|
|
|
319
|
-
A stream is
|
|
320
|
-
-
|
|
321
|
-
-
|
|
299
|
+
A TCP stream is classified as **sustained** (elephant flow) when:
|
|
300
|
+
- Active for **>10 seconds**, AND
|
|
301
|
+
- Average throughput exceeds **20 Mbit/s** (2.5 MB/s)
|
|
322
302
|
|
|
323
|
-
Once classified,
|
|
303
|
+
Once classified, its flow control window locks to 1 MB and data frames move to the lowest-priority queue.
|
|
324
304
|
|
|
325
305
|
### Adaptive Per-Stream Windows
|
|
326
306
|
|
|
327
|
-
Each stream has a send window
|
|
307
|
+
Each TCP stream has a send window from a shared **200 MB budget**:
|
|
328
308
|
|
|
329
309
|
| Active Streams | Window per Stream |
|
|
330
310
|
|---|---|
|
|
331
311
|
| 1–50 | 4 MB (maximum) |
|
|
332
|
-
| 51–
|
|
312
|
+
| 51–200 | Scales down (4 MB → 1 MB) |
|
|
333
313
|
| 200+ | 1 MB (floor) |
|
|
334
314
|
|
|
335
|
-
|
|
315
|
+
UDP traffic uses no flow control — datagrams are fire-and-forget, matching UDP semantics.
|
|
336
316
|
|
|
337
317
|
## 💡 Example Scenarios
|
|
338
318
|
|
|
339
|
-
### 1. Expose a Private
|
|
319
|
+
### 1. Expose a Private Cluster to the Internet
|
|
340
320
|
|
|
341
|
-
Deploy an Edge on a public VPS, point
|
|
321
|
+
Deploy an Edge on a public VPS, point DNS to its IP. The Edge tunnels all TCP and UDP traffic to the Hub running inside your private cluster. No public ports needed on the cluster.
|
|
342
322
|
|
|
343
323
|
### 2. Multi-Region Edge Ingress
|
|
344
324
|
|
|
345
|
-
Run
|
|
325
|
+
Run Edges in NYC, Frankfurt, and Tokyo — all connecting to a single Hub. Use GeoDNS to route users to their nearest Edge. PROXY protocol ensures the Hub sees real client IPs regardless of which Edge they entered through.
|
|
326
|
+
|
|
327
|
+
### 3. UDP Forwarding (DNS, Gaming, VoIP)
|
|
328
|
+
|
|
329
|
+
Configure UDP listen ports alongside TCP ports. DNS queries, game server traffic, or VoIP packets are tunneled through the same edge/hub connection and forwarded to SmartProxy with a PROXY v2 binary header preserving the client's real IP.
|
|
346
330
|
|
|
347
|
-
|
|
331
|
+
```typescript
|
|
332
|
+
await hub.updateAllowedEdges([
|
|
333
|
+
{
|
|
334
|
+
id: 'edge-nyc-01',
|
|
335
|
+
secret: 'secret',
|
|
336
|
+
listenPorts: [80, 443], // TCP
|
|
337
|
+
listenPortsUdp: [53, 27015], // DNS + game server
|
|
338
|
+
},
|
|
339
|
+
]);
|
|
340
|
+
```
|
|
348
341
|
|
|
349
|
-
|
|
342
|
+
### 4. QUIC Transport for Low-Latency
|
|
350
343
|
|
|
351
|
-
|
|
344
|
+
Use QUIC transport to eliminate head-of-line blocking — a lost packet on one stream doesn't stall others. QUIC also enables 0-RTT reconnection and connection migration.
|
|
352
345
|
|
|
353
|
-
|
|
346
|
+
```typescript
|
|
347
|
+
await edge.start({
|
|
348
|
+
hubHost: 'hub.example.com',
|
|
349
|
+
hubPort: 8443,
|
|
350
|
+
edgeId: 'edge-01',
|
|
351
|
+
secret: 'secret',
|
|
352
|
+
transportMode: 'quicWithFallback', // try QUIC, fall back to TLS if UDP blocked
|
|
353
|
+
});
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### 5. Token-Based Edge Provisioning
|
|
357
|
+
|
|
358
|
+
Generate connection tokens on the hub side and distribute them to edge operators:
|
|
354
359
|
|
|
355
360
|
```typescript
|
|
356
|
-
// Hub operator generates token
|
|
357
361
|
const token = encodeConnectionToken({
|
|
358
362
|
hubHost: 'hub.prod.example.com',
|
|
359
363
|
hubPort: 8443,
|
|
360
364
|
edgeId: 'edge-tokyo-01',
|
|
361
365
|
secret: 'generated-secret-abc123',
|
|
362
366
|
});
|
|
363
|
-
// Send `token` to the edge operator
|
|
367
|
+
// Send `token` to the edge operator — a single string is all they need
|
|
364
368
|
|
|
365
|
-
// Edge operator starts with just the token
|
|
366
369
|
const edge = new RemoteIngressEdge();
|
|
367
370
|
await edge.start({ token });
|
|
368
371
|
```
|
|
369
372
|
|
|
370
|
-
### 5. Dynamic Port Management
|
|
371
|
-
|
|
372
|
-
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.
|
|
373
|
-
|
|
374
|
-
```typescript
|
|
375
|
-
// Initially assign ports 80 and 443
|
|
376
|
-
await hub.updateAllowedEdges([
|
|
377
|
-
{ id: 'edge-nyc-01', secret: 'secret', listenPorts: [80, 443] },
|
|
378
|
-
]);
|
|
379
|
-
|
|
380
|
-
// Later, add port 8080 — the connected edge picks it up instantly
|
|
381
|
-
await hub.updateAllowedEdges([
|
|
382
|
-
{ id: 'edge-nyc-01', secret: 'secret', listenPorts: [80, 443, 8080] },
|
|
383
|
-
]);
|
|
384
|
-
```
|
|
385
|
-
|
|
386
373
|
## License and Legal Information
|
|
387
374
|
|
|
388
375
|
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./LICENSE) file.
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@serve.zone/remoteingress',
|
|
6
|
-
version: '4.
|
|
7
|
-
description: 'Edge ingress tunnel for DcRouter -
|
|
6
|
+
version: '4.13.0',
|
|
7
|
+
description: 'Edge ingress tunnel for DcRouter - tunnels TCP and UDP traffic from the network edge to SmartProxy over TLS or QUIC, preserving client IP via PROXY protocol.'
|
|
8
8
|
}
|