feat(device-plane): harden edge channel rotation

This commit is contained in:
Codex
2026-08-11 19:37:32 +03:00
parent 50b9179fe4
commit 1124c15216
7 changed files with 365 additions and 34 deletions
@@ -94,6 +94,66 @@ test("keeps the channel alive and reconnects without losing idempotency", async
}
});
test("rotates the Edge certificate through staged overlap and rejects retired identity", async () => {
const edge = createEdgeServer();
const address = await edge.start();
let registration = edgeRegistration(address, [
{
generationRef: "trust-generation:1",
fingerprint: certificates.edge.fingerprint,
status: "active",
},
{
generationRef: "trust-generation:2",
fingerprint: certificates.edgeNext.fingerprint,
status: "staged",
},
]);
const core = createCoreClient({
address,
registrationProvider: async () => registration,
});
try {
await core.start();
await core.waitForReady(2_000);
await waitFor(() => edge.status().channel === "accepted", 2_000);
assert.equal(core.status().edgeTrustGeneration, "trust-generation:1");
edge.rotateTrust({
generationRef: "trust-generation:2",
key: certificates.edgeNext.key,
cert: certificates.edgeNext.cert,
ca: certificates.ca,
allowedCoreFingerprints: [certificates.core.fingerprint],
});
await waitFor(() => core.status().connectionAttempts >= 2
&& core.status().channel === "accepted"
&& core.status().edgeTrustGeneration === "trust-generation:2"
&& edge.status().channel === "accepted", 2_000);
registration = edgeRegistration(address, [{
generationRef: "trust-generation:2",
fingerprint: certificates.edgeNext.fingerprint,
status: "active",
}]);
assert.equal((await edge.submitAdapterMessage(adapterMessage())).status, "accepted");
const failuresBeforeRollback = core.status().protocolFailures;
edge.rotateTrust({
generationRef: "trust-generation:3",
key: certificates.edge.key,
cert: certificates.edge.cert,
ca: certificates.ca,
allowedCoreFingerprints: [certificates.core.fingerprint],
});
await waitFor(() => core.status().protocolFailures > failuresBeforeRollback, 2_000);
assert.notEqual(core.status().channel, "accepted");
} finally {
await core.stop();
await edge.stop();
}
});
test("rejects a revoked Edge registration before opening a channel", async () => {
const edge = createEdgeServer();
const address = await edge.start();
@@ -167,6 +227,92 @@ test("returns a conclusive rejection when Core cannot accept a package", async (
}
});
test("isolates tracker session ordering while allowing cross-session progress", async () => {
let releaseSlow;
const slowGate = new Promise((resolve) => {
releaseSlow = resolve;
});
const calls = [];
const pair = await startPair({
acceptMessage: async (message) => {
calls.push(message.sessionRef);
if (message.sessionRef === "session:slow") await slowGate;
return acceptanceFor(message, false);
},
});
try {
let slowResolved = false;
const slow = pair.edge.submitAdapterMessage(adapterMessage({
sessionRef: "session:slow",
messageRef: "message:slow-1",
idempotencyKey: `sha256:${"c".repeat(64)}`,
})).then((value) => {
slowResolved = true;
return value;
});
await waitFor(() => calls.includes("session:slow"), 500);
const fast = await pair.edge.submitAdapterMessage(adapterMessage({
sessionRef: "session:fast",
messageRef: "message:fast-1",
idempotencyKey: `sha256:${"d".repeat(64)}`,
}));
assert.equal(fast.status, "accepted");
assert.equal(slowResolved, false);
releaseSlow();
assert.equal((await slow).status, "accepted");
} finally {
releaseSlow?.();
await stopPair(pair);
}
});
test("preserves per-session order and applies a bounded acceptance window", async () => {
let releaseFirst;
const firstGate = new Promise((resolve) => {
releaseFirst = resolve;
});
const calls = [];
const pair = await startPair({
maxPendingAcceptances: 2,
acceptMessage: async (message) => {
calls.push(message.messageRef);
if (message.messageRef === "message:ordered-1") await firstGate;
return acceptanceFor(message, false);
},
});
try {
const first = pair.edge.submitAdapterMessage(adapterMessage({
sessionRef: "session:ordered",
messageRef: "message:ordered-1",
idempotencyKey: `sha256:${"e".repeat(64)}`,
sequence: 1,
}));
await waitFor(() => calls.length === 1, 500);
const second = pair.edge.submitAdapterMessage(adapterMessage({
sessionRef: "session:ordered",
messageRef: "message:ordered-2",
idempotencyKey: `sha256:${"f".repeat(64)}`,
sequence: 2,
}));
await delay(30);
assert.deepEqual(calls, ["message:ordered-1"]);
await assert.rejects(
pair.edge.submitAdapterMessage(adapterMessage({
sessionRef: "session:overflow",
messageRef: "message:overflow-1",
idempotencyKey: `sha256:${"1".repeat(64)}`,
})),
/acceptance_window_full/,
);
releaseFirst();
await Promise.all([first, second]);
assert.deepEqual(calls, ["message:ordered-1", "message:ordered-2"]);
} finally {
releaseFirst?.();
await stopPair(pair);
}
});
test("closes the logical session on an unknown message kind", async () => {
const edge = createEdgeServer();
const address = await edge.start();
@@ -221,34 +367,41 @@ async function startPair(options = {}) {
}
function createEdgeServer(options = {}) {
const edgeCertificate = options.edgeCertificate ?? certificates.edge;
return createDeviceEdgeChannelServer({
edgeRegistrationId: "edge:pilot-1",
channelGeneration: "generation:pilot-1",
trustGeneration: options.edgeTrustGeneration ?? "trust-generation:1",
host: "127.0.0.1",
port: 0,
tls: {
key: certificates.edge.key,
cert: certificates.edge.cert,
key: edgeCertificate.key,
cert: edgeCertificate.cert,
ca: certificates.ca,
allowedCoreFingerprints: [certificates.core.fingerprint],
allowedCoreFingerprints: options.allowedCoreFingerprints
?? [certificates.core.fingerprint],
},
keepaliveMs: options.keepaliveMs ?? 50,
deadPeerMs: options.deadPeerMs ?? 150,
acceptanceTimeoutMs: 500,
maxPendingAcceptances: options.maxPendingAcceptances,
});
}
function createCoreClient(options) {
const clientCertificate = options.clientCertificate ?? certificates.core;
return createDeviceGatewayCoreChannelClient({
registration: {
edgeRegistrationId: "edge:pilot-1",
endpoint: `https://127.0.0.1:${options.address.port}/`,
servername: "localhost",
certificateFingerprint: options.expectedEdgeFingerprint
const registration = edgeRegistration(options.address,
options.edgeCertificateIdentities ?? [{
generationRef: "trust-generation:1",
fingerprint: options.expectedEdgeFingerprint
?? certificates.edge.fingerprint,
lifecycleState: options.lifecycleState ?? "active",
},
status: "active",
}],
options.lifecycleState ?? "active");
return createDeviceGatewayCoreChannelClient({
...(options.registrationProvider
? { registrationProvider: options.registrationProvider }
: { registration }),
tls: {
key: clientCertificate.key,
cert: clientCertificate.cert,
@@ -271,6 +424,16 @@ function createCoreClient(options) {
});
}
function edgeRegistration(address, certificateIdentities, lifecycleState = "active") {
return {
edgeRegistrationId: "edge:pilot-1",
endpoint: `https://127.0.0.1:${address.port}/`,
servername: "localhost",
certificateIdentities,
lifecycleState,
};
}
async function stopPair(pair) {
await pair.core.stop();
await pair.edge.stop();
@@ -293,7 +456,7 @@ function discoverySignal() {
};
}
function adapterMessage() {
function adapterMessage(overrides = {}) {
return {
schemaVersion: DEVICE_ADAPTER_MESSAGE_SCHEMA,
edgeRef: "edge:pilot-1",
@@ -314,6 +477,7 @@ function adapterMessage() {
byteLength: 11,
packageDigest: `sha256:${"b".repeat(64)}`,
},
...overrides,
};
}
@@ -340,6 +504,10 @@ async function generateCertificateFixture(directory) {
"subjectAltName=DNS:localhost,IP:127.0.0.1",
"extendedKeyUsage=serverAuth",
]);
await issueCertificate(directory, "edge-next", "localhost", [
"subjectAltName=DNS:localhost,IP:127.0.0.1",
"extendedKeyUsage=serverAuth",
]);
await issueCertificate(directory, "core", "nodedc-device-gateway-core", [
"extendedKeyUsage=clientAuth",
]);
@@ -350,6 +518,7 @@ async function generateCertificateFixture(directory) {
return {
ca,
edge: await readCertificate(directory, "edge"),
edgeNext: await readCertificate(directory, "edge-next"),
core: await readCertificate(directory, "core"),
intruder: await readCertificate(directory, "intruder"),
};