feat(device-plane): enable B2 discovery ingress

This commit is contained in:
Codex
2026-07-26 00:52:31 +03:00
parent 3c538ad98c
commit 2b1795509b
19 changed files with 2375 additions and 156 deletions
@@ -4,6 +4,15 @@ import test from "node:test";
import { createDeviceGatewayRuntime } from "../src/runtime.mjs";
const specificationHeader = Buffer.from(
"FF23E9EF782DE7120300",
"hex",
);
const specificationPackage = Buffer.from(
"5B01010000FBDEC251EC5D",
"hex",
);
test("baseline health exposes no public ingress and no command transport", async () => {
const runtime = createDeviceGatewayRuntime({
healthPort: 0,
@@ -25,37 +34,93 @@ test("baseline health exposes no public ingress and no command transport", async
}
});
test("loopback evidence listener emits no acknowledgement or command bytes", async () => {
test("discovery-only ingress persists HEADER2 before acknowledging packages", async () => {
const captured = [];
const runtime = createDeviceGatewayRuntime({
healthPort: 0,
tcpHost: "0.0.0.0",
tcpPort: 0,
listenEnabled: true,
publicIngressEnabled: true,
now: () => new Date(0x52db95de * 1000),
onDiscovery: async (value) => captured.push(value),
});
const addresses = await runtime.start();
const client = await connectAndCollect(addresses.tcpAddress.port);
try {
client.socket.write(specificationHeader.subarray(0, 4));
await new Promise((resolve) => setImmediate(resolve));
assert.equal(client.bytes().length, 0);
client.socket.write(specificationHeader.subarray(4));
await client.waitForBytes(9);
assert.equal(
client.bytes().subarray(0, 9).toString("hex").toUpperCase(),
"7B0400A0DE95DB527D",
);
assert.equal(captured.length, 1);
assert.equal(captured[0].identifier.value, "865209039777769");
assert.equal(captured[0].evidence.framingStatus, "verified");
assert.equal(captured[0].commandTransport, undefined);
client.socket.write(specificationPackage);
await client.waitForBytes(13);
assert.equal(
client.bytes().subarray(9).toString("hex").toUpperCase(),
"7B00017D",
);
assert.equal(runtime.status().totalDiscoveries, 1);
assert.equal(runtime.status().totalPackagesAcknowledged, 1);
assert.equal(runtime.status().commandTransport, "disabled");
assert.equal(runtime.status().publicIngress, "discovery-only");
const response = await fetch(
`http://127.0.0.1:${addresses.healthAddress.port}/healthz`,
);
const body = await response.json();
assert.equal(body.framing, "verified-read-only");
assert.equal(body.tcpListener, "discovery-only");
assert.equal(body.publicIngress, "discovery-only");
assert.equal(body.commandTransport, "disabled");
} finally {
client.socket.destroy();
await runtime.stop();
}
});
test("does not acknowledge malformed or unverified initial bytes", async () => {
const captured = [];
const runtime = createDeviceGatewayRuntime({
healthPort: 0,
tcpPort: 0,
listenEnabled: true,
onEvidence: (value) => captured.push(value),
onDiscovery: async (value) => captured.push(value),
});
const addresses = await runtime.start();
try {
const received = await sendAndCollect(
addresses.tcpAddress.port,
Buffer.from(
"unverified-frame-with-fake-identifier-000000000000001",
"utf8",
),
Buffer.from("not-a-b2-header", "utf8"),
);
assert.equal(received.length, 0);
assert.equal(captured.length, 1);
assert.equal(captured[0].evidence.identifierExtracted, false);
assert.equal(
JSON.stringify(captured).includes("000000000000001"),
false,
);
assert.equal(runtime.status().totalEvidence, 1);
assert.equal(runtime.status().commandTransport, "disabled");
assert.equal(captured.length, 0);
assert.equal(runtime.status().totalRejected, 1);
} finally {
await runtime.stop();
}
});
test("public ingress requires an authenticated discovery sink", () => {
assert.throws(
() => createDeviceGatewayRuntime({
listenEnabled: true,
publicIngressEnabled: true,
tcpHost: "0.0.0.0",
}),
/device_gateway_discovery_sink_required/,
);
});
test("baseline rejects non-loopback binding", () => {
assert.throws(
() => createDeviceGatewayRuntime({
@@ -66,7 +131,7 @@ test("baseline rejects non-loopback binding", () => {
);
});
test("container health may bind all interfaces while TCP stays loopback-only", async () => {
test("container health may bind all interfaces while TCP stays disabled", async () => {
const runtime = createDeviceGatewayRuntime({
healthHost: "0.0.0.0",
healthPort: 0,
@@ -82,6 +147,40 @@ test("container health may bind all interfaces while TCP stays loopback-only", a
}
});
function connectAndCollect(port) {
return new Promise((resolve, reject) => {
const chunks = [];
let byteLength = 0;
const waiters = [];
const socket = connect({ host: "127.0.0.1", port }, () => {
resolve({
socket,
bytes: () => Buffer.concat(chunks, byteLength),
waitForBytes: (minimum) => {
if (byteLength >= minimum) return Promise.resolve();
return new Promise((waitResolve, waitReject) => {
waiters.push({ minimum, waitResolve, waitReject });
});
},
});
});
socket.on("data", (chunk) => {
chunks.push(chunk);
byteLength += chunk.length;
for (let index = waiters.length - 1; index >= 0; index -= 1) {
if (byteLength >= waiters[index].minimum) {
waiters[index].waitResolve();
waiters.splice(index, 1);
}
}
});
socket.on("error", (error) => {
for (const waiter of waiters.splice(0)) waiter.waitReject(error);
reject(error);
});
});
}
function sendAndCollect(port, payload) {
return new Promise((resolve, reject) => {
const chunks = [];
@@ -89,7 +188,7 @@ function sendAndCollect(port, payload) {
socket.end(payload);
});
socket.on("data", (chunk) => chunks.push(chunk));
socket.on("end", () => resolve(Buffer.concat(chunks)));
socket.on("close", () => resolve(Buffer.concat(chunks)));
socket.on("error", reject);
});
}