mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-13 20:27:22 +00:00
6893ece898
Lays the dormant data-plane foundation for Sencho Mesh. The pilot tunnel gains TCP forwarding frames (tcp_open / tcp_open_ack / tcp_close JSON plus a 0x04 TcpData binary type) and a TcpStream surface on the bridge so a future MeshService can ride the existing WSS tunnel for cross-node container traffic. The agent rejects every tcp_open with mesh_not_enabled until a follow-up PR wires the Dockerode resolver gated by a mesh_stacks opt-in table; ships dormant. A new top-level mesh-sidecar/ package provides the per-node container that will host the L4 forwarder + control WS in production. Built as a small Node 22 alpine image and published in lockstep with the main sencho image via a parallel docker-publish workflow job. Tests cover protocol roundtrips on both packages and the sidecar forwarder end-to-end including resolve, splice, close, and stats.
59 lines
1.9 KiB
TypeScript
59 lines
1.9 KiB
TypeScript
import { ControlClient } from './control';
|
|
import { Forwarder } from './forwarder';
|
|
|
|
const SIDECAR_VERSION = '0.0.1';
|
|
|
|
function requireEnv(name: string): string {
|
|
const value = process.env[name];
|
|
if (!value) {
|
|
console.error(`[mesh-sidecar] missing required env: ${name}`);
|
|
process.exit(1);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
function main(): void {
|
|
const controlUrl = requireEnv('SENCHO_CONTROL_URL');
|
|
const token = requireEnv('SENCHO_MESH_TOKEN');
|
|
const nodeIdRaw = requireEnv('MESH_NODE_ID');
|
|
const nodeId = Number.parseInt(nodeIdRaw, 10);
|
|
if (!Number.isFinite(nodeId)) {
|
|
console.error('[mesh-sidecar] MESH_NODE_ID must be an integer');
|
|
process.exit(1);
|
|
}
|
|
|
|
let client: ControlClient | null = null;
|
|
// Bridge with explicit named params so types stay tight; client is wired
|
|
// immediately after construction so the null check is only racing against
|
|
// the 5s stats timer first tick.
|
|
const forwarder = new Forwarder({
|
|
resolve: (connId, port, remoteAddr) => client?.resolve(connId, port, remoteAddr),
|
|
sendData: (streamId, payload) => client?.sendData(streamId, payload),
|
|
sendClose: (streamId) => client?.sendClose(streamId),
|
|
sendStats: (streamId, bytesIn, bytesOut, lastActivity) =>
|
|
client?.sendStats(streamId, bytesIn, bytesOut, lastActivity),
|
|
});
|
|
forwarder.start();
|
|
|
|
client = new ControlClient({
|
|
controlUrl,
|
|
token,
|
|
nodeId,
|
|
sidecarVersion: SIDECAR_VERSION,
|
|
forwarder,
|
|
});
|
|
client.start();
|
|
|
|
const shutdown = async () => {
|
|
await forwarder.shutdown();
|
|
await client?.shutdown();
|
|
process.exit(0);
|
|
};
|
|
process.on('SIGTERM', () => { void shutdown(); });
|
|
process.on('SIGINT', () => { void shutdown(); });
|
|
|
|
console.log(`[mesh-sidecar] started for node=${nodeId} version=${SIDECAR_VERSION}`);
|
|
}
|
|
|
|
main();
|