feat(pilot): add tcp tunnel frames + mesh sidecar package (#857)

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.
This commit is contained in:
Anso
2026-05-01 00:28:18 -04:00
committed by GitHub
parent b8437e8780
commit 6893ece898
17 changed files with 2706 additions and 6 deletions
+34 -2
View File
@@ -23,6 +23,7 @@ export enum BinaryFrameType {
HttpReqBody = 0x01,
HttpResBody = 0x02,
WsMessageBinary = 0x03,
TcpData = 0x04,
}
// --- JSON envelope types ---
@@ -39,7 +40,10 @@ export type JsonFrame =
| WsRejectFrame
| WsMessageTextFrame
| WsCloseFrame
| ControlFrame;
| ControlFrame
| TcpOpenFrame
| TcpOpenAckFrame
| TcpCloseFrame;
export interface HelloFrame {
t: 'hello';
@@ -119,6 +123,33 @@ export interface ControlFrame {
payload?: Record<string, unknown>;
}
/**
* Sencho Mesh TCP frames. The primary asks the agent to open a TCP connection
* to a Compose service on the agent's local Docker host. Bytes flow as
* BinaryFrameType.TcpData. Mid-stream failures send tcp_close.
*/
export interface TcpOpenFrame {
t: 'tcp_open';
s: number;
stack: string;
service: string;
port: number;
}
export type MeshErrCode = 'mesh_not_enabled' | 'denied' | 'no_target' | 'unreachable' | 'agent_error';
export interface TcpOpenAckFrame {
t: 'tcp_open_ack';
s: number;
ok: boolean;
err?: MeshErrCode;
}
export interface TcpCloseFrame {
t: 'tcp_close';
s: number;
}
// --- Serialize / parse ---
export function encodeJsonFrame(frame: JsonFrame): string {
@@ -161,7 +192,8 @@ export function decodeBinaryFrame(buf: Buffer): DecodedBinaryFrame {
const type = buf.readUInt8(0) as BinaryFrameType;
if (type !== BinaryFrameType.HttpReqBody &&
type !== BinaryFrameType.HttpResBody &&
type !== BinaryFrameType.WsMessageBinary) {
type !== BinaryFrameType.WsMessageBinary &&
type !== BinaryFrameType.TcpData) {
throw new Error(`unknown binary frame type: ${type}`);
}
const streamId = buf.readUInt32BE(1);