@serve.zone/remoteingress 3.0.2 → 3.1.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.0.2',
6
+ version: '3.1.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=
@@ -4,8 +4,6 @@ export interface IEdgeConfig {
4
4
  hubPort?: number;
5
5
  edgeId: string;
6
6
  secret: string;
7
- listenPorts: number[];
8
- stunIntervalSecs?: number;
9
7
  }
10
8
  export declare class RemoteIngressEdge extends EventEmitter {
11
9
  private bridge;
@@ -13,8 +11,11 @@ export declare class RemoteIngressEdge extends EventEmitter {
13
11
  constructor();
14
12
  /**
15
13
  * Start the edge — spawns the Rust binary and connects to the hub.
14
+ * Accepts either a connection token or an explicit IEdgeConfig.
16
15
  */
17
- start(config: IEdgeConfig): Promise<void>;
16
+ start(config: {
17
+ token: string;
18
+ } | IEdgeConfig): Promise<void>;
18
19
  /**
19
20
  * Stop the edge and kill the Rust process.
20
21
  */
@@ -1,5 +1,6 @@
1
1
  import * as plugins from './plugins.js';
2
2
  import { EventEmitter } from 'events';
3
+ import { decodeConnectionToken } from './classes.token.js';
3
4
  export class RemoteIngressEdge extends EventEmitter {
4
5
  constructor() {
5
6
  super();
@@ -11,9 +12,13 @@ export class RemoteIngressEdge extends EventEmitter {
11
12
  requestTimeoutMs: 30_000,
12
13
  readyTimeoutMs: 10_000,
13
14
  localPaths: [
14
- plugins.path.join(packageDir, 'dist_rust'),
15
- plugins.path.join(packageDir, 'rust', 'target', 'release'),
16
- plugins.path.join(packageDir, 'rust', 'target', 'debug'),
15
+ // Platform-suffixed binary in dist_rust (production)
16
+ plugins.path.join(packageDir, 'dist_rust', `remoteingress-bin_${process.platform === 'win32' ? 'windows' : 'linux'}_${process.arch === 'x64' ? 'amd64' : process.arch}`),
17
+ // Exact binaryName fallback in dist_rust
18
+ plugins.path.join(packageDir, 'dist_rust', 'remoteingress-bin'),
19
+ // Development build paths (cargo output uses exact name)
20
+ plugins.path.join(packageDir, 'rust', 'target', 'release', 'remoteingress-bin'),
21
+ plugins.path.join(packageDir, 'rust', 'target', 'debug', 'remoteingress-bin'),
17
22
  ],
18
23
  searchSystemPath: false,
19
24
  });
@@ -30,19 +35,31 @@ export class RemoteIngressEdge extends EventEmitter {
30
35
  }
31
36
  /**
32
37
  * Start the edge — spawns the Rust binary and connects to the hub.
38
+ * Accepts either a connection token or an explicit IEdgeConfig.
33
39
  */
34
40
  async start(config) {
41
+ let edgeConfig;
42
+ if ('token' in config) {
43
+ const decoded = decodeConnectionToken(config.token);
44
+ edgeConfig = {
45
+ hubHost: decoded.hubHost,
46
+ hubPort: decoded.hubPort,
47
+ edgeId: decoded.edgeId,
48
+ secret: decoded.secret,
49
+ };
50
+ }
51
+ else {
52
+ edgeConfig = config;
53
+ }
35
54
  const spawned = await this.bridge.spawn();
36
55
  if (!spawned) {
37
56
  throw new Error('Failed to spawn remoteingress-bin');
38
57
  }
39
58
  await this.bridge.sendCommand('startEdge', {
40
- hubHost: config.hubHost,
41
- hubPort: config.hubPort ?? 8443,
42
- edgeId: config.edgeId,
43
- secret: config.secret,
44
- listenPorts: config.listenPorts,
45
- stunIntervalSecs: config.stunIntervalSecs,
59
+ hubHost: edgeConfig.hubHost,
60
+ hubPort: edgeConfig.hubPort ?? 8443,
61
+ edgeId: edgeConfig.edgeId,
62
+ secret: edgeConfig.secret,
46
63
  });
47
64
  this.started = true;
48
65
  }
@@ -74,4 +91,4 @@ export class RemoteIngressEdge extends EventEmitter {
74
91
  return this.bridge.running;
75
92
  }
76
93
  }
77
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5yZW1vdGVpbmdyZXNzZWRnZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2NsYXNzZXMucmVtb3RlaW5ncmVzc2VkZ2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFDeEMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLFFBQVEsQ0FBQztBQTRDdEMsTUFBTSxPQUFPLGlCQUFrQixTQUFRLFlBQVk7SUFJakQ7UUFDRSxLQUFLLEVBQUUsQ0FBQztRQUhGLFlBQU8sR0FBRyxLQUFLLENBQUM7UUFLdEIsTUFBTSxVQUFVLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQ3JDLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksR0FBRyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsUUFBUSxDQUFDLEVBQ3ZELElBQUksQ0FDTCxDQUFDO1FBRUYsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLE9BQU8sQ0FBQyxTQUFTLENBQUMsVUFBVSxDQUFnQjtZQUM1RCxVQUFVLEVBQUUsbUJBQW1CO1lBQy9CLE9BQU8sRUFBRSxDQUFDLGNBQWMsQ0FBQztZQUN6QixnQkFBZ0IsRUFBRSxNQUFNO1lBQ3hCLGNBQWMsRUFBRSxNQUFNO1lBQ3RCLFVBQVUsRUFBRTtnQkFDVixPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUUsV0FBVyxDQUFDO2dCQUMxQyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxTQUFTLENBQUM7Z0JBQzFELE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxNQUFNLEVBQUUsUUFBUSxFQUFFLE9BQU8sQ0FBQzthQUN6RDtZQUNELGdCQUFnQixFQUFFLEtBQUs7U0FDeEIsQ0FBQyxDQUFDO1FBRUgsa0NBQWtDO1FBQ2xDLElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLDRCQUE0QixFQUFFLEdBQUcsRUFBRTtZQUNoRCxJQUFJLENBQUMsSUFBSSxDQUFDLGlCQUFpQixDQUFDLENBQUM7UUFDL0IsQ0FBQyxDQUFDLENBQUM7UUFDSCxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQywrQkFBK0IsRUFBRSxHQUFHLEVBQUU7WUFDbkQsSUFBSSxDQUFDLElBQUksQ0FBQyxvQkFBb0IsQ0FBQyxDQUFDO1FBQ2xDLENBQUMsQ0FBQyxDQUFDO1FBQ0gsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsK0JBQStCLEVBQUUsQ0FBQyxJQUFvQixFQUFFLEVBQUU7WUFDdkUsSUFBSSxDQUFDLElBQUksQ0FBQyxvQkFBb0IsRUFBRSxJQUFJLENBQUMsQ0FBQztRQUN4QyxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxLQUFLLENBQUMsTUFBbUI7UUFDcEMsTUFBTSxPQUFPLEdBQUcsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQzFDLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUNiLE1BQU0sSUFBSSxLQUFLLENBQUMsbUNBQW1DLENBQUMsQ0FBQztRQUN2RCxDQUFDO1FBRUQsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQyxXQUFXLEVBQUU7WUFDekMsT0FBTyxFQUFFLE1BQU0sQ0FBQyxPQUFPO1lBQ3ZCLE9BQU8sRUFBRSxNQUFNLENBQUMsT0FBTyxJQUFJLElBQUk7WUFDL0IsTUFBTSxFQUFFLE1BQU0sQ0FBQyxNQUFNO1lBQ3JCLE1BQU0sRUFBRSxNQUFNLENBQUMsTUFBTTtZQUNyQixXQUFXLEVBQUUsTUFBTSxDQUFDLFdBQVc7WUFDL0IsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDLGdCQUFnQjtTQUMxQyxDQUFDLENBQUM7UUFFSCxJQUFJLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQztJQUN0QixDQUFDO0lBRUQ7O09BRUc7SUFDSSxLQUFLLENBQUMsSUFBSTtRQUNmLElBQUksSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ2pCLElBQUksQ0FBQztnQkFDSCxNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLFVBQVUsRUFBRSxFQUEyQixDQUFDLENBQUM7WUFDekUsQ0FBQztZQUFDLE1BQU0sQ0FBQztnQkFDUCw4QkFBOEI7WUFDaEMsQ0FBQztZQUNELElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUM7WUFDbkIsSUFBSSxDQUFDLE9BQU8sR0FBRyxLQUFLLENBQUM7UUFDdkIsQ0FBQztJQUNILENBQUM7SUFFRDs7T0FFRztJQUNJLEtBQUssQ0FBQyxTQUFTO1FBQ3BCLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsZUFBZSxFQUFFLEVBQTJCLENBQUMsQ0FBQztJQUMvRSxDQUFDO0lBRUQ7O09BRUc7SUFDSCxJQUFXLE9BQU87UUFDaEIsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQztJQUM3QixDQUFDO0NBQ0YifQ==
94
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5yZW1vdGVpbmdyZXNzZWRnZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2NsYXNzZXMucmVtb3RlaW5ncmVzc2VkZ2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFDeEMsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUN0QyxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQXdDM0QsTUFBTSxPQUFPLGlCQUFrQixTQUFRLFlBQVk7SUFJakQ7UUFDRSxLQUFLLEVBQUUsQ0FBQztRQUhGLFlBQU8sR0FBRyxLQUFLLENBQUM7UUFLdEIsTUFBTSxVQUFVLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQ3JDLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksR0FBRyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsUUFBUSxDQUFDLEVBQ3ZELElBQUksQ0FDTCxDQUFDO1FBRUYsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLE9BQU8sQ0FBQyxTQUFTLENBQUMsVUFBVSxDQUFnQjtZQUM1RCxVQUFVLEVBQUUsbUJBQW1CO1lBQy9CLE9BQU8sRUFBRSxDQUFDLGNBQWMsQ0FBQztZQUN6QixnQkFBZ0IsRUFBRSxNQUFNO1lBQ3hCLGNBQWMsRUFBRSxNQUFNO1lBQ3RCLFVBQVUsRUFBRTtnQkFDVixxREFBcUQ7Z0JBQ3JELE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxXQUFXLEVBQUUscUJBQXFCLE9BQU8sQ0FBQyxRQUFRLEtBQUssT0FBTyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLE9BQU8sSUFBSSxPQUFPLENBQUMsSUFBSSxLQUFLLEtBQUssQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMs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
@@ -11,9 +11,13 @@ export class RemoteIngressHub extends EventEmitter {
11
11
  requestTimeoutMs: 30_000,
12
12
  readyTimeoutMs: 10_000,
13
13
  localPaths: [
14
- plugins.path.join(packageDir, 'dist_rust'),
15
- plugins.path.join(packageDir, 'rust', 'target', 'release'),
16
- plugins.path.join(packageDir, 'rust', 'target', 'debug'),
14
+ // Platform-suffixed binary in dist_rust (production)
15
+ plugins.path.join(packageDir, 'dist_rust', `remoteingress-bin_${process.platform === 'win32' ? 'windows' : 'linux'}_${process.arch === 'x64' ? 'amd64' : process.arch}`),
16
+ // Exact binaryName fallback in dist_rust
17
+ plugins.path.join(packageDir, 'dist_rust', 'remoteingress-bin'),
18
+ // Development build paths (cargo output uses exact name)
19
+ plugins.path.join(packageDir, 'rust', 'target', 'release', 'remoteingress-bin'),
20
+ plugins.path.join(packageDir, 'rust', 'target', 'debug', 'remoteingress-bin'),
17
21
  ],
18
22
  searchSystemPath: false,
19
23
  });
@@ -79,4 +83,4 @@ export class RemoteIngressHub extends EventEmitter {
79
83
  return this.bridge.running;
80
84
  }
81
85
  }
82
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5yZW1vdGVpbmdyZXNzaHViLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvY2xhc3Nlcy5yZW1vdGVpbmdyZXNzaHViLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxRQUFRLENBQUM7QUE0Q3RDLE1BQU0sT0FBTyxnQkFBaUIsU0FBUSxZQUFZO0lBSWhEO1FBQ0UsS0FBSyxFQUFFLENBQUM7UUFIRixZQUFPLEdBQUcsS0FBSyxDQUFDO1FBS3RCLE1BQU0sVUFBVSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUNyQyxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLEdBQUcsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxFQUN2RCxJQUFJLENBQ0wsQ0FBQztRQUVGLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxPQUFPLENBQUMsU0FBUyxDQUFDLFVBQVUsQ0FBZTtZQUMzRCxVQUFVLEVBQUUsbUJBQW1CO1lBQy9CLE9BQU8sRUFBRSxDQUFDLGNBQWMsQ0FBQztZQUN6QixnQkFBZ0IsRUFBRSxNQUFNO1lBQ3hCLGNBQWMsRUFBRSxNQUFNO1lBQ3RCLFVBQVUsRUFBRTtnQkFDVixPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUUsV0FBVyxDQUFDO2dCQUMxQyxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxTQUFTLENBQUM7Z0JBQzFELE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxNQUFNLEVBQUUsUUFBUSxFQUFFLE9BQU8sQ0FBQzthQUN6RDtZQUNELGdCQUFnQixFQUFFLEtBQUs7U0FDeEIsQ0FBQyxDQUFDO1FBRUgsa0NBQWtD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
86
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5yZW1vdGVpbmdyZXNzaHViLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvY2xhc3Nlcy5yZW1vdGVpbmdyZXNzaHViLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxRQUFRLENBQUM7QUE0Q3RDLE1BQU0sT0FBTyxnQkFBaUIsU0FBUSxZQUFZO0lBSWhEO1FBQ0UsS0FBSyxFQUFFLENBQUM7UUFIRixZQUFPLEdBQUcsS0FBSyxDQUFDO1FBS3RCLE1BQU0sVUFBVSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUNyQyxPQUFPLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLEdBQUcsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxFQUN2RCxJQUFJLENBQ0wsQ0FBQztRQUVGLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxPQUFPLENBQUMsU0FBUyxDQUFDLFVBQVUsQ0FBZTtZQUMzRCxVQUFVLEVBQUUsbUJBQW1CO1lBQy9CLE9BQU8sRUFBRSxDQUFDLGNBQWMsQ0FBQztZQUN6QixnQkFBZ0IsRUFBRSxNQUFNO1lBQ3hCLGNBQWMsRUFBRSxNQUFNO1lBQ3RCLFVBQVUsRUFBRTtnQkFDVixxREFBcUQ7Z0JBQ3JELE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxXQUFXLEVBQUUscUJBQXFCLE9BQU8sQ0FBQyxRQUFRLEtBQUssT0FBTyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLE9BQU8sSUFBSSxPQUFPLENBQUMsSUFBSSxLQUFLLEtBQUssQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLENBQUM7Z0JBQ3hLLHlDQUF5QztnQkFDekMsT0FBTyxDQUFDLElB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
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Connection token utilities for RemoteIngress edge connections.
3
+ * A token is a base64url-encoded compact JSON object carrying hub connection details.
4
+ */
5
+ export interface IConnectionTokenData {
6
+ hubHost: string;
7
+ hubPort: number;
8
+ edgeId: string;
9
+ secret: string;
10
+ }
11
+ /**
12
+ * Encode connection data into a single opaque token string (base64url).
13
+ */
14
+ export declare function encodeConnectionToken(data: IConnectionTokenData): string;
15
+ /**
16
+ * Decode a connection token back into its constituent fields.
17
+ * Throws on malformed or incomplete tokens.
18
+ */
19
+ export declare function decodeConnectionToken(token: string): IConnectionTokenData;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Connection token utilities for RemoteIngress edge connections.
3
+ * A token is a base64url-encoded compact JSON object carrying hub connection details.
4
+ */
5
+ /**
6
+ * Encode connection data into a single opaque token string (base64url).
7
+ */
8
+ export function encodeConnectionToken(data) {
9
+ const compact = JSON.stringify({
10
+ h: data.hubHost,
11
+ p: data.hubPort,
12
+ e: data.edgeId,
13
+ s: data.secret,
14
+ });
15
+ // base64url: standard base64 with + → -, / → _, trailing = stripped
16
+ return Buffer.from(compact, 'utf-8')
17
+ .toString('base64')
18
+ .replace(/\+/g, '-')
19
+ .replace(/\//g, '_')
20
+ .replace(/=+$/, '');
21
+ }
22
+ /**
23
+ * Decode a connection token back into its constituent fields.
24
+ * Throws on malformed or incomplete tokens.
25
+ */
26
+ export function decodeConnectionToken(token) {
27
+ let parsed;
28
+ try {
29
+ // Restore standard base64 from base64url
30
+ let base64 = token.replace(/-/g, '+').replace(/_/g, '/');
31
+ // Re-add padding
32
+ const remainder = base64.length % 4;
33
+ if (remainder === 2)
34
+ base64 += '==';
35
+ else if (remainder === 3)
36
+ base64 += '=';
37
+ const json = Buffer.from(base64, 'base64').toString('utf-8');
38
+ parsed = JSON.parse(json);
39
+ }
40
+ catch {
41
+ throw new Error('Invalid connection token');
42
+ }
43
+ if (typeof parsed.h !== 'string' ||
44
+ typeof parsed.p !== 'number' ||
45
+ typeof parsed.e !== 'string' ||
46
+ typeof parsed.s !== 'string') {
47
+ throw new Error('Invalid connection token: missing required fields');
48
+ }
49
+ return {
50
+ hubHost: parsed.h,
51
+ hubPort: parsed.p,
52
+ edgeId: parsed.e,
53
+ secret: parsed.s,
54
+ };
55
+ }
56
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy50b2tlbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2NsYXNzZXMudG9rZW4udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBU0g7O0dBRUc7QUFDSCxNQUFNLFVBQVUscUJBQXFCLENBQUMsSUFBMEI7SUFDOUQsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQztRQUM3QixDQUFDLEVBQUUsSUFBSSxDQUFDLE9BQU87UUFDZixDQUFDLEVBQUUsSUFBSSxDQUFDLE9BQU87UUFDZixDQUFDLEVBQUUsSUFBSSxDQUFDLE1BQU07UUFDZCxDQUFDLEVBQUUsSUFBSSxDQUFDLE1BQU07S0FDZixDQUFDLENBQUM7SUFDSCxvRUFBb0U7SUFDcEUsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxPQUFPLENBQUM7U0FDakMsUUFBUSxDQUFDLFFBQVEsQ0FBQztTQUNsQixPQUFPLENBQUMsS0FBSyxFQUFFLEdBQUcsQ0FBQztTQUNuQixPQUFPLENBQUMsS0FBSyxFQUFFLEdBQUcsQ0FBQztTQUNuQixPQUFPLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBQ3hCLENBQUM7QUFFRDs7O0dBR0c7QUFDSCxNQUFNLFVBQVUscUJBQXFCLENBQUMsS0FBYTtJQUNqRCxJQUFJLE1BQThELENBQUM7SUFDbkUsSUFBSSxDQUFDO1FBQ0gseUNBQXlDO1FBQ3pDLElBQUksTUFBTSxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFDekQsaUJBQWlCO1FBQ2pCLE1BQU0sU0FBUyxHQUFHLE1BQU0sQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDO1FBQ3BDLElBQUksU0FBUyxLQUFLLENBQUM7WUFBRSxNQUFNLElBQUksSUFBSSxDQUFDO2FBQy9CLElBQUksU0FBUyxLQUFLLENBQUM7WUFBRSxNQUFNLElBQUksR0FBRyxDQUFDO1FBRXhDLE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLFFBQVEsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUM3RCxNQUFNLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUM1QixDQUFDO0lBQUMsTUFBTSxDQUFDO1FBQ1AsTUFBTSxJQUFJLEtBQUssQ0FBQywwQkFBMEIsQ0FBQyxDQUFDO0lBQzlDLENBQUM7SUFFRCxJQUNFLE9BQU8sTUFBTSxDQUFDLENBQUMsS0FBSyxRQUFRO1FBQzVCLE9BQU8sTUFBTSxDQUFDLENBQUMsS0FBSyxRQUFRO1FBQzVCLE9BQU8sTUFBTSxDQUFDLENBQUMsS0FBSyxRQUFRO1FBQzVCLE9BQU8sTUFBTSxDQUFDLENBQUMsS0FBSyxRQUFRLEVBQzVCLENBQUM7UUFDRCxNQUFNLElBQUksS0FBSyxDQUFDLG1EQUFtRCxDQUFDLENBQUM7SUFDdkUsQ0FBQztJQUVELE9BQU87UUFDTCxPQUFPLEVBQUUsTUFBTSxDQUFDLENBQUM7UUFDakIsT0FBTyxFQUFFLE1BQU0sQ0FBQyxDQUFDO1FBQ2pCLE1BQU0sRUFBRSxNQUFNLENBQUMsQ0FBQztRQUNoQixNQUFNLEVBQUUsTUFBTSxDQUFDLENBQUM7S0FDakIsQ0FBQztBQUNKLENBQUMifQ==
@@ -1,2 +1,3 @@
1
1
  export * from './classes.remoteingresshub.js';
2
2
  export * from './classes.remoteingressedge.js';
3
+ export * from './classes.token.js';
package/dist_ts/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from './classes.remoteingresshub.js';
2
2
  export * from './classes.remoteingressedge.js';
3
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLCtCQUErQixDQUFDO0FBQzlDLGNBQWMsZ0NBQWdDLENBQUMifQ==
3
+ export * from './classes.token.js';
4
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLCtCQUErQixDQUFDO0FBQzlDLGNBQWMsZ0NBQWdDLENBQUM7QUFDL0MsY0FBYyxvQkFBb0IsQ0FBQyJ9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@serve.zone/remoteingress",
3
- "version": "3.0.2",
3
+ "version": "3.1.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",
@@ -10,20 +10,20 @@
10
10
  "license": "MIT",
11
11
  "scripts": {
12
12
  "test": "(tstest test/ --web)",
13
- "build": "(tsbuild --web --allowimplicitany && tsrust)",
13
+ "build": "(tsbuild tsfolders --allowimplicitany && tsrust)",
14
14
  "buildDocs": "(tsdoc)"
15
15
  },
16
16
  "devDependencies": {
17
- "@git.zone/tsbuild": "^2.1.25",
18
- "@git.zone/tsbundle": "^2.0.5",
19
- "@git.zone/tsrun": "^1.2.46",
17
+ "@git.zone/tsbuild": "^4.1.2",
18
+ "@git.zone/tsbundle": "^2.8.3",
19
+ "@git.zone/tsrun": "^2.0.1",
20
20
  "@git.zone/tsrust": "^1.3.0",
21
- "@git.zone/tstest": "^1.0.44",
22
- "@push.rocks/tapbundle": "^5.0.15",
23
- "@types/node": "^20.8.7"
21
+ "@git.zone/tstest": "^3.1.8",
22
+ "@push.rocks/tapbundle": "^6.0.3",
23
+ "@types/node": "^25.2.3"
24
24
  },
25
25
  "dependencies": {
26
- "@push.rocks/qenv": "^6.0.5",
26
+ "@push.rocks/qenv": "^6.1.3",
27
27
  "@push.rocks/smartrust": "^1.2.1"
28
28
  },
29
29
  "repository": {
package/readme.hints.md CHANGED
@@ -7,3 +7,6 @@
7
7
  * STUN-based public IP discovery at the edge (Cloudflare STUN server)
8
8
  * Cross-compiled Rust binary for linux/amd64 and linux/arm64
9
9
  * Old `ConnectorPublic`/`ConnectorPrivate` classes no longer exist (removed in v2.0.0/v3.0.0)
10
+ * `localPaths` in RustBridge config must be full file paths (not directories) — smartrust's `RustBinaryLocator` checks `isExecutable()` on each entry directly
11
+ * Production binaries are named with platform suffixes: `remoteingress-bin_linux_amd64`, `remoteingress-bin_linux_arm64`
12
+ * Rust core uses `ring` as the rustls CryptoProvider (explicitly installed in main.rs, aws-lc-rs disabled via default-features=false)
package/readme.md CHANGED
@@ -2,19 +2,15 @@
2
2
 
3
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.
4
4
 
5
- ## Issue Reporting and Security
6
-
7
- For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
8
-
9
5
  ## Install
10
6
 
11
7
  ```sh
12
8
  pnpm install @serve.zone/remoteingress
13
9
  ```
14
10
 
15
- ## 🏗️ Architecture
11
+ ## Architecture
16
12
 
17
- `@serve.zone/remoteingress` uses a **HubEdge** topology with a high-performance Rust core and a TypeScript API surface:
13
+ `@serve.zone/remoteingress` uses a **Hub/Edge** topology with a high-performance Rust core and a TypeScript API surface:
18
14
 
19
15
  ```
20
16
  ┌─────────────────────┐ TLS Tunnel ┌─────────────────────┐
@@ -36,16 +32,16 @@ pnpm install @serve.zone/remoteingress
36
32
  | **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. |
37
33
  | **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
34
 
39
- ### 🔑 Key Features
35
+ ### Key Features
40
36
 
41
- - 🔒 **TLS-encrypted tunnel** between edge and hub (auto-generated self-signed cert or bring your own)
42
- - 🔀 **Multiplexed streams** — thousands of client connections flow over a single tunnel
43
- - 🌐 **PROXY protocol v1** — SmartProxy sees the real client IP, not the tunnel IP
44
- - 🔐 **Shared-secret authentication** — edges must present valid credentials to connect
45
- - 📡 **STUN-based public IP discovery** — the edge automatically discovers its public IP via Cloudflare STUN
46
- - 🔄 **Auto-reconnect** with exponential backoff if the tunnel drops
47
- - 📢 **Event-driven** — both Hub and Edge extend `EventEmitter` for real-time monitoring
48
- - **Rust core** — all frame encoding, TLS, and TCP proxying happen in native code for maximum throughput
37
+ - **TLS-encrypted tunnel** between edge and hub (auto-generated self-signed cert or bring your own)
38
+ - **Multiplexed streams** — thousands of client connections flow over a single tunnel
39
+ - **PROXY protocol v1** — SmartProxy sees the real client IP, not the tunnel IP
40
+ - **Shared-secret authentication** — edges must present valid credentials to connect
41
+ - **STUN-based public IP discovery** — the edge automatically discovers its public IP via Cloudflare STUN
42
+ - **Auto-reconnect** with exponential backoff if the tunnel drops
43
+ - **Event-driven** — both Hub and Edge extend `EventEmitter` for real-time monitoring
44
+ - **Rust core** — all frame encoding, TLS, and TCP proxying happen in native code for maximum throughput
49
45
 
50
46
  ## Usage
51
47
 
@@ -60,21 +56,21 @@ const hub = new RemoteIngressHub();
60
56
 
61
57
  // Listen for events
62
58
  hub.on('edgeConnected', ({ edgeId }) => {
63
- console.log(`✅ Edge ${edgeId} connected`);
59
+ console.log(`Edge ${edgeId} connected`);
64
60
  });
65
61
  hub.on('edgeDisconnected', ({ edgeId }) => {
66
- console.log(`❌ Edge ${edgeId} disconnected`);
62
+ console.log(`Edge ${edgeId} disconnected`);
67
63
  });
68
64
  hub.on('streamOpened', ({ edgeId, streamId }) => {
69
- console.log(`🔗 Stream ${streamId} opened from edge ${edgeId}`);
65
+ console.log(`Stream ${streamId} opened from edge ${edgeId}`);
70
66
  });
71
67
  hub.on('streamClosed', ({ edgeId, streamId }) => {
72
- console.log(`🔗 Stream ${streamId} closed from edge ${edgeId}`);
68
+ console.log(`Stream ${streamId} closed from edge ${edgeId}`);
73
69
  });
74
70
 
75
71
  // Start the hub — it will listen for incoming edge TLS connections
76
72
  await hub.start({
77
- tunnelPort: 8443, // port edges connect to (default: 8443)
73
+ tunnelPort: 8443, // port edges connect to (default: 8443)
78
74
  targetHost: '127.0.0.1', // SmartProxy host to forward streams to (default: 127.0.0.1)
79
75
  });
80
76
 
@@ -108,13 +104,13 @@ const edge = new RemoteIngressEdge();
108
104
 
109
105
  // Listen for events
110
106
  edge.on('tunnelConnected', () => {
111
- console.log('🟢 Tunnel to hub established');
107
+ console.log('Tunnel to hub established');
112
108
  });
113
109
  edge.on('tunnelDisconnected', () => {
114
- console.log('🔴 Tunnel to hub lost — will auto-reconnect');
110
+ console.log('Tunnel to hub lost — will auto-reconnect');
115
111
  });
116
112
  edge.on('publicIpDiscovered', ({ ip }) => {
117
- console.log(`🌐 Public IP: ${ip}`);
113
+ console.log(`Public IP: ${ip}`);
118
114
  });
119
115
 
120
116
  // Start the edge — it connects to the hub and starts listening for clients
@@ -124,12 +120,12 @@ await edge.start({
124
120
  edgeId: 'edge-nyc-01', // unique edge identifier
125
121
  secret: 'supersecrettoken1', // must match the hub's allowed edge secret
126
122
  listenPorts: [80, 443], // public ports to accept TCP connections on
127
- stunIntervalSecs: 300, // STUN refresh interval in seconds (default: 300)
123
+ stunIntervalSecs: 300, // STUN refresh interval in seconds (optional)
128
124
  });
129
125
 
130
126
  // Check status at any time
131
- const status = await edge.getStatus();
132
- console.log(status);
127
+ const edgeStatus = await edge.getStatus();
128
+ console.log(edgeStatus);
133
129
  // {
134
130
  // running: true,
135
131
  // connected: true,
@@ -167,7 +163,7 @@ await edge.stop();
167
163
 
168
164
  **Events:** `tunnelConnected`, `tunnelDisconnected`, `publicIpDiscovered`
169
165
 
170
- ### 🔌 Wire Protocol
166
+ ### Wire Protocol
171
167
 
172
168
  The tunnel uses a custom binary frame protocol over TLS:
173
169
 
@@ -177,11 +173,11 @@ The tunnel uses a custom binary frame protocol over TLS:
177
173
 
178
174
  | Frame Type | Value | Direction | Purpose |
179
175
  |------------|-------|-----------|---------|
180
- | `OPEN` | `0x01` | Edge Hub | Open a new stream; payload is PROXY v1 header |
181
- | `DATA` | `0x02` | Edge Hub | Client data flowing upstream |
182
- | `CLOSE` | `0x03` | Edge Hub | Client closed the connection |
183
- | `DATA_BACK` | `0x04` | Hub Edge | Response data flowing downstream |
184
- | `CLOSE_BACK` | `0x05` | Hub Edge | Upstream (SmartProxy) closed the connection |
176
+ | `OPEN` | `0x01` | Edge -> Hub | Open a new stream; payload is PROXY v1 header |
177
+ | `DATA` | `0x02` | Edge -> Hub | Client data flowing upstream |
178
+ | `CLOSE` | `0x03` | Edge -> Hub | Client closed the connection |
179
+ | `DATA_BACK` | `0x04` | Hub -> Edge | Response data flowing downstream |
180
+ | `CLOSE_BACK` | `0x05` | Hub -> Edge | Upstream (SmartProxy) closed the connection |
185
181
 
186
182
  Max payload size per frame: **16 MB**.
187
183
 
@@ -195,21 +191,19 @@ Max payload size per frame: **16 MB**.
195
191
 
196
192
  ## License and Legal Information
197
193
 
198
- This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./LICENSE) file.
194
+ This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
199
195
 
200
196
  **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.
201
197
 
202
198
  ### Trademarks
203
199
 
204
- This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
205
-
206
- Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
200
+ This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.
207
201
 
208
202
  ### Company Information
209
203
 
210
204
  Task Venture Capital GmbH
211
- Registered at District Court Bremen HRB 35230 HB, Germany
205
+ Registered at District court Bremen HRB 35230 HB, Germany
212
206
 
213
- For any legal inquiries or further information, please contact us via email at hello@task.vc.
207
+ For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
214
208
 
215
209
  By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@serve.zone/remoteingress',
6
- version: '3.0.2',
6
+ version: '3.1.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
  }
@@ -1,5 +1,6 @@
1
1
  import * as plugins from './plugins.js';
2
2
  import { EventEmitter } from 'events';
3
+ import { decodeConnectionToken } from './classes.token.js';
3
4
 
4
5
  // Command map for the edge side of remoteingress-bin
5
6
  type TEdgeCommands = {
@@ -13,8 +14,6 @@ type TEdgeCommands = {
13
14
  hubPort: number;
14
15
  edgeId: string;
15
16
  secret: string;
16
- listenPorts: number[];
17
- stunIntervalSecs?: number;
18
17
  };
19
18
  result: { started: boolean };
20
19
  };
@@ -39,8 +38,6 @@ export interface IEdgeConfig {
39
38
  hubPort?: number;
40
39
  edgeId: string;
41
40
  secret: string;
42
- listenPorts: number[];
43
- stunIntervalSecs?: number;
44
41
  }
45
42
 
46
43
  export class RemoteIngressEdge extends EventEmitter {
@@ -61,9 +58,13 @@ export class RemoteIngressEdge extends EventEmitter {
61
58
  requestTimeoutMs: 30_000,
62
59
  readyTimeoutMs: 10_000,
63
60
  localPaths: [
64
- plugins.path.join(packageDir, 'dist_rust'),
65
- plugins.path.join(packageDir, 'rust', 'target', 'release'),
66
- plugins.path.join(packageDir, 'rust', 'target', 'debug'),
61
+ // Platform-suffixed binary in dist_rust (production)
62
+ plugins.path.join(packageDir, 'dist_rust', `remoteingress-bin_${process.platform === 'win32' ? 'windows' : 'linux'}_${process.arch === 'x64' ? 'amd64' : process.arch}`),
63
+ // Exact binaryName fallback in dist_rust
64
+ plugins.path.join(packageDir, 'dist_rust', 'remoteingress-bin'),
65
+ // Development build paths (cargo output uses exact name)
66
+ plugins.path.join(packageDir, 'rust', 'target', 'release', 'remoteingress-bin'),
67
+ plugins.path.join(packageDir, 'rust', 'target', 'debug', 'remoteingress-bin'),
67
68
  ],
68
69
  searchSystemPath: false,
69
70
  });
@@ -82,20 +83,33 @@ export class RemoteIngressEdge extends EventEmitter {
82
83
 
83
84
  /**
84
85
  * Start the edge — spawns the Rust binary and connects to the hub.
86
+ * Accepts either a connection token or an explicit IEdgeConfig.
85
87
  */
86
- public async start(config: IEdgeConfig): Promise<void> {
88
+ public async start(config: { token: string } | IEdgeConfig): Promise<void> {
89
+ let edgeConfig: IEdgeConfig;
90
+
91
+ if ('token' in config) {
92
+ const decoded = decodeConnectionToken(config.token);
93
+ edgeConfig = {
94
+ hubHost: decoded.hubHost,
95
+ hubPort: decoded.hubPort,
96
+ edgeId: decoded.edgeId,
97
+ secret: decoded.secret,
98
+ };
99
+ } else {
100
+ edgeConfig = config;
101
+ }
102
+
87
103
  const spawned = await this.bridge.spawn();
88
104
  if (!spawned) {
89
105
  throw new Error('Failed to spawn remoteingress-bin');
90
106
  }
91
107
 
92
108
  await this.bridge.sendCommand('startEdge', {
93
- hubHost: config.hubHost,
94
- hubPort: config.hubPort ?? 8443,
95
- edgeId: config.edgeId,
96
- secret: config.secret,
97
- listenPorts: config.listenPorts,
98
- stunIntervalSecs: config.stunIntervalSecs,
109
+ hubHost: edgeConfig.hubHost,
110
+ hubPort: edgeConfig.hubPort ?? 8443,
111
+ edgeId: edgeConfig.edgeId,
112
+ secret: edgeConfig.secret,
99
113
  });
100
114
 
101
115
  this.started = true;
@@ -61,9 +61,13 @@ export class RemoteIngressHub extends EventEmitter {
61
61
  requestTimeoutMs: 30_000,
62
62
  readyTimeoutMs: 10_000,
63
63
  localPaths: [
64
- plugins.path.join(packageDir, 'dist_rust'),
65
- plugins.path.join(packageDir, 'rust', 'target', 'release'),
66
- plugins.path.join(packageDir, 'rust', 'target', 'debug'),
64
+ // Platform-suffixed binary in dist_rust (production)
65
+ plugins.path.join(packageDir, 'dist_rust', `remoteingress-bin_${process.platform === 'win32' ? 'windows' : 'linux'}_${process.arch === 'x64' ? 'amd64' : process.arch}`),
66
+ // Exact binaryName fallback in dist_rust
67
+ plugins.path.join(packageDir, 'dist_rust', 'remoteingress-bin'),
68
+ // Development build paths (cargo output uses exact name)
69
+ plugins.path.join(packageDir, 'rust', 'target', 'release', 'remoteingress-bin'),
70
+ plugins.path.join(packageDir, 'rust', 'target', 'debug', 'remoteingress-bin'),
67
71
  ],
68
72
  searchSystemPath: false,
69
73
  });
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Connection token utilities for RemoteIngress edge connections.
3
+ * A token is a base64url-encoded compact JSON object carrying hub connection details.
4
+ */
5
+
6
+ export interface IConnectionTokenData {
7
+ hubHost: string;
8
+ hubPort: number;
9
+ edgeId: string;
10
+ secret: string;
11
+ }
12
+
13
+ /**
14
+ * Encode connection data into a single opaque token string (base64url).
15
+ */
16
+ export function encodeConnectionToken(data: IConnectionTokenData): string {
17
+ const compact = JSON.stringify({
18
+ h: data.hubHost,
19
+ p: data.hubPort,
20
+ e: data.edgeId,
21
+ s: data.secret,
22
+ });
23
+ // base64url: standard base64 with + → -, / → _, trailing = stripped
24
+ return Buffer.from(compact, 'utf-8')
25
+ .toString('base64')
26
+ .replace(/\+/g, '-')
27
+ .replace(/\//g, '_')
28
+ .replace(/=+$/, '');
29
+ }
30
+
31
+ /**
32
+ * Decode a connection token back into its constituent fields.
33
+ * Throws on malformed or incomplete tokens.
34
+ */
35
+ export function decodeConnectionToken(token: string): IConnectionTokenData {
36
+ let parsed: { h?: unknown; p?: unknown; e?: unknown; s?: unknown };
37
+ try {
38
+ // Restore standard base64 from base64url
39
+ let base64 = token.replace(/-/g, '+').replace(/_/g, '/');
40
+ // Re-add padding
41
+ const remainder = base64.length % 4;
42
+ if (remainder === 2) base64 += '==';
43
+ else if (remainder === 3) base64 += '=';
44
+
45
+ const json = Buffer.from(base64, 'base64').toString('utf-8');
46
+ parsed = JSON.parse(json);
47
+ } catch {
48
+ throw new Error('Invalid connection token');
49
+ }
50
+
51
+ if (
52
+ typeof parsed.h !== 'string' ||
53
+ typeof parsed.p !== 'number' ||
54
+ typeof parsed.e !== 'string' ||
55
+ typeof parsed.s !== 'string'
56
+ ) {
57
+ throw new Error('Invalid connection token: missing required fields');
58
+ }
59
+
60
+ return {
61
+ hubHost: parsed.h,
62
+ hubPort: parsed.p,
63
+ edgeId: parsed.e,
64
+ secret: parsed.s,
65
+ };
66
+ }
package/ts/index.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from './classes.remoteingresshub.js';
2
2
  export * from './classes.remoteingressedge.js';
3
+ export * from './classes.token.js';