BREAKING CHANGE(remoteingress): migrate core to Rust, add RemoteIngressHub/RemoteIngressEdge JS bridge, and bump package to v2.0.0

This commit is contained in:
2026-02-16 11:22:23 +00:00
parent a3970edf23
commit a144f5a798
25 changed files with 11564 additions and 3408 deletions

View File

@@ -1,8 +1,8 @@
/**
* autocreated commitinfo by @pushrocks/commitinfo
* autocreated commitinfo by @push.rocks/commitinfo
*/
export const commitinfo = {
name: '@serve.zone/remoteingress',
version: '1.0.4',
description: 'Provides a service for creating private tunnels and reaching private clusters from the outside, facilitating secure remote access as part of the @serve.zone stack.'
version: '3.0.0',
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.'
}

View File

@@ -0,0 +1,132 @@
import * as plugins from './plugins.js';
import { EventEmitter } from 'events';
// Command map for the edge side of remoteingress-bin
type TEdgeCommands = {
ping: {
params: Record<string, never>;
result: { pong: boolean };
};
startEdge: {
params: {
hubHost: string;
hubPort: number;
edgeId: string;
secret: string;
listenPorts: number[];
stunIntervalSecs?: number;
};
result: { started: boolean };
};
stopEdge: {
params: Record<string, never>;
result: { stopped: boolean; wasRunning?: boolean };
};
getEdgeStatus: {
params: Record<string, never>;
result: {
running: boolean;
connected: boolean;
publicIp: string | null;
activeStreams: number;
listenPorts: number[];
};
};
};
export interface IEdgeConfig {
hubHost: string;
hubPort?: number;
edgeId: string;
secret: string;
listenPorts: number[];
stunIntervalSecs?: number;
}
export class RemoteIngressEdge extends EventEmitter {
private bridge: InstanceType<typeof plugins.smartrust.RustBridge<TEdgeCommands>>;
private started = false;
constructor() {
super();
const packageDir = plugins.path.resolve(
plugins.path.dirname(new URL(import.meta.url).pathname),
'..',
);
this.bridge = new plugins.smartrust.RustBridge<TEdgeCommands>({
binaryName: 'remoteingress-bin',
cliArgs: ['--management'],
requestTimeoutMs: 30_000,
readyTimeoutMs: 10_000,
localPaths: [
plugins.path.join(packageDir, 'dist_rust'),
plugins.path.join(packageDir, 'rust', 'target', 'release'),
plugins.path.join(packageDir, 'rust', 'target', 'debug'),
],
searchSystemPath: false,
});
// Forward events from Rust binary
this.bridge.on('management:tunnelConnected', () => {
this.emit('tunnelConnected');
});
this.bridge.on('management:tunnelDisconnected', () => {
this.emit('tunnelDisconnected');
});
this.bridge.on('management:publicIpDiscovered', (data: { ip: string }) => {
this.emit('publicIpDiscovered', data);
});
}
/**
* Start the edge — spawns the Rust binary and connects to the hub.
*/
public async start(config: IEdgeConfig): Promise<void> {
const spawned = await this.bridge.spawn();
if (!spawned) {
throw new Error('Failed to spawn remoteingress-bin');
}
await this.bridge.sendCommand('startEdge', {
hubHost: config.hubHost,
hubPort: config.hubPort ?? 8443,
edgeId: config.edgeId,
secret: config.secret,
listenPorts: config.listenPorts,
stunIntervalSecs: config.stunIntervalSecs,
});
this.started = true;
}
/**
* Stop the edge and kill the Rust process.
*/
public async stop(): Promise<void> {
if (this.started) {
try {
await this.bridge.sendCommand('stopEdge', {} as Record<string, never>);
} catch {
// Process may already be dead
}
this.bridge.kill();
this.started = false;
}
}
/**
* Get the current edge status.
*/
public async getStatus() {
return this.bridge.sendCommand('getEdgeStatus', {} as Record<string, never>);
}
/**
* Check if the bridge is running.
*/
public get running(): boolean {
return this.bridge.running;
}
}

View File

@@ -0,0 +1,138 @@
import * as plugins from './plugins.js';
import { EventEmitter } from 'events';
// Command map for the hub side of remoteingress-bin
type THubCommands = {
ping: {
params: Record<string, never>;
result: { pong: boolean };
};
startHub: {
params: {
tunnelPort: number;
targetHost?: string;
};
result: { started: boolean };
};
stopHub: {
params: Record<string, never>;
result: { stopped: boolean; wasRunning?: boolean };
};
updateAllowedEdges: {
params: {
edges: Array<{ id: string; secret: string }>;
};
result: { updated: boolean };
};
getHubStatus: {
params: Record<string, never>;
result: {
running: boolean;
tunnelPort: number;
connectedEdges: Array<{
edgeId: string;
connectedAt: number;
activeStreams: number;
}>;
};
};
};
export interface IHubConfig {
tunnelPort?: number;
targetHost?: string;
}
export class RemoteIngressHub extends EventEmitter {
private bridge: InstanceType<typeof plugins.smartrust.RustBridge<THubCommands>>;
private started = false;
constructor() {
super();
const packageDir = plugins.path.resolve(
plugins.path.dirname(new URL(import.meta.url).pathname),
'..',
);
this.bridge = new plugins.smartrust.RustBridge<THubCommands>({
binaryName: 'remoteingress-bin',
cliArgs: ['--management'],
requestTimeoutMs: 30_000,
readyTimeoutMs: 10_000,
localPaths: [
plugins.path.join(packageDir, 'dist_rust'),
plugins.path.join(packageDir, 'rust', 'target', 'release'),
plugins.path.join(packageDir, 'rust', 'target', 'debug'),
],
searchSystemPath: false,
});
// Forward events from Rust binary
this.bridge.on('management:edgeConnected', (data: { edgeId: string }) => {
this.emit('edgeConnected', data);
});
this.bridge.on('management:edgeDisconnected', (data: { edgeId: string }) => {
this.emit('edgeDisconnected', data);
});
this.bridge.on('management:streamOpened', (data: { edgeId: string; streamId: number }) => {
this.emit('streamOpened', data);
});
this.bridge.on('management:streamClosed', (data: { edgeId: string; streamId: number }) => {
this.emit('streamClosed', data);
});
}
/**
* Start the hub — spawns the Rust binary and starts the tunnel server.
*/
public async start(config: IHubConfig = {}): Promise<void> {
const spawned = await this.bridge.spawn();
if (!spawned) {
throw new Error('Failed to spawn remoteingress-bin');
}
await this.bridge.sendCommand('startHub', {
tunnelPort: config.tunnelPort ?? 8443,
targetHost: config.targetHost ?? '127.0.0.1',
});
this.started = true;
}
/**
* Stop the hub and kill the Rust process.
*/
public async stop(): Promise<void> {
if (this.started) {
try {
await this.bridge.sendCommand('stopHub', {} as Record<string, never>);
} catch {
// Process may already be dead
}
this.bridge.kill();
this.started = false;
}
}
/**
* Update the list of allowed edges that can connect to this hub.
*/
public async updateAllowedEdges(edges: Array<{ id: string; secret: string }>): Promise<void> {
await this.bridge.sendCommand('updateAllowedEdges', { edges });
}
/**
* Get the current hub status.
*/
public async getStatus() {
return this.bridge.sendCommand('getHubStatus', {} as Record<string, never>);
}
/**
* Check if the bridge is running.
*/
public get running(): boolean {
return this.bridge.running;
}
}

View File

@@ -1,38 +0,0 @@
import * as plugins from './plugins.js';
export class ConnectorPrivate {
private targetHost: string;
private targetPort: number;
constructor(targetHost: string, targetPort: number = 4000) {
this.targetHost = targetHost;
this.targetPort = targetPort;
this.connectToPublicRemoteConnector();
}
private connectToPublicRemoteConnector(): void {
const options = {
// Include CA certificate if necessary, for example:
// ca: fs.readFileSync('path/to/ca.pem'),
rejectUnauthorized: true // Only set this to true if you are sure about the server's certificate
};
const tunnel = plugins.tls.connect(this.targetPort, options, () => {
console.log('Connected to PublicRemoteConnector on port 4000');
});
tunnel.on('data', (data: Buffer) => {
const targetConnection = plugins.tls.connect({
host: this.targetHost,
port: this.targetPort,
// Include necessary options for the target connection
}, () => {
targetConnection.write(data);
});
targetConnection.on('data', (backData: Buffer) => {
tunnel.write(backData); // Send data back through the tunnel
});
});
}
}

View File

@@ -1,45 +0,0 @@
import * as plugins from './plugins.js';
export class ConnectorPublic {
private tunnel: plugins.tls.TLSSocket | null = null;
constructor() {
this.createTunnel();
this.listenOnPorts();
}
private createTunnel(): void {
const options = {
key: plugins.fs.readFileSync('path/to/key.pem'),
cert: plugins.fs.readFileSync('path/to/cert.pem'),
};
const server = plugins.tls.createServer(options, (socket: plugins.tls.TLSSocket) => {
this.tunnel = socket;
console.log('Tunnel established with LocalConnector');
});
server.listen(4000, () => {
console.log('PublicRemoteConnector listening for tunnel on port 4000');
});
}
private listenOnPorts(): void {
// Example for port 80, adapt for port 443 similarly
// Note: TLS for the initial connection might not apply directly for HTTP/HTTPS traffic without additional setup
const options = {
key: plugins.fs.readFileSync('path/to/key.pem'),
cert: plugins.fs.readFileSync('path/to/cert.pem'),
};
plugins.tls.createServer(options, (socket: plugins.tls.TLSSocket) => {
console.log('Received connection, tunneling to LocalConnector');
if (this.tunnel) {
socket.pipe(this.tunnel).pipe(socket);
} else {
console.log('Tunnel to LocalConnector not established');
socket.end();
}
}).listen(80); // Repeat this block for any other ports you wish to listen on
}
}

View File

@@ -1,14 +1,2 @@
import * as plugins from './plugins.js';
import { ConnectorPublic } from './connector.public.js';
import { ConnectorPrivate } from './connector.private.js';
export {
ConnectorPublic,
ConnectorPrivate
}
export const runCli = async () => {
const qenv = new plugins.qenv.Qenv();
const mode = await qenv.getEnvVarOnDemand('MODE');
}
export * from './classes.remoteingresshub.js';
export * from './classes.remoteingressedge.js';

View File

@@ -1,15 +1,7 @@
// node native scope
import * as tls from 'tls';
import * as fs from 'fs';
export {
tls,
fs,
}
import * as path from 'path';
export { path };
// @push.rocks scope
import * as qenv from '@push.rocks/qenv';
export {
qenv,
}
import * as smartrust from '@push.rocks/smartrust';
export { smartrust };