feat: establish standalone Device Core repository

This commit is contained in:
DCCONSTRUCTIONS
2026-08-21 11:51:21 +03:00
commit e0bac205d0
244 changed files with 51962 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
{
"name": "@nodedc/device-edge-channel",
"version": "0.1.0",
"private": true,
"type": "module",
"exports": {
".": "./src/runtime.mjs"
},
"engines": {
"node": ">=20"
}
}
@@ -0,0 +1,616 @@
import { randomUUID, X509Certificate } from "node:crypto";
import { createSecureServer } from "node:http2";
import {
DEVICE_EDGE_CHANNEL_LIMITS,
DEVICE_EDGE_CHANNEL_PATH,
createChannelEnvelope,
createChannelEnvelopeDecoder,
encodeChannelEnvelope,
normalizeCertificateFingerprint,
} from "../../../packages/device-edge-channel-contract/src/index.mjs";
import {
normalizeAdapterAcceptance,
normalizeAdapterMessage,
normalizeDiscoverySignal,
} from "../../../packages/device-protocol-contract/src/index.mjs";
const CHANNEL_TRACKER_SESSION_ID = "channel:control";
const CHANNEL_PROFILE_REF = "channel.control.v1";
export function createDeviceEdgeChannelServer(options = {}) {
const config = normalizeConfig(options);
let trust = config.trust;
const pending = new Map();
let active = null;
let started = false;
let heartbeatTimer = null;
let totalChannelsAccepted = 0;
let totalChannelsRejected = 0;
let totalEventsSubmitted = 0;
let totalEventsAccepted = 0;
let totalEventsRejected = 0;
let totalProtocolFailures = 0;
const server = createSecureServer({
key: trust.key,
cert: trust.cert,
ca: trust.ca,
minVersion: "TLSv1.3",
maxVersion: "TLSv1.3",
allowHTTP1: false,
requestCert: true,
rejectUnauthorized: true,
settings: {
enablePush: false,
maxConcurrentStreams: 1,
initialWindowSize: 1024 * 1024,
},
});
server.on("stream", (stream, headers) => {
if (headers[":method"] !== "POST" || headers[":path"] !== DEVICE_EDGE_CHANNEL_PATH) {
stream.respond({ ":status": 404, "cache-control": "no-store" });
stream.end();
return;
}
if (!isApprovedCorePeer(stream)) {
totalChannelsRejected += 1;
stream.respond({ ":status": 403, "cache-control": "no-store" });
stream.end();
return;
}
if (active) {
totalChannelsRejected += 1;
stream.respond({ ":status": 409, "cache-control": "no-store" });
stream.end();
return;
}
const state = {
stream,
decoder: createChannelEnvelopeDecoder({
direction: "core-to-edge",
maxEnvelopeBytes: config.maxEnvelopeBytes,
}),
accepted: false,
edgeSequence: 0,
coreSequence: 0,
lastCoreActivityAt: config.clock(),
closed: false,
};
active = state;
stream.respond({
":status": 200,
"content-type": "application/x-ndjson",
"cache-control": "no-store",
"x-content-type-options": "nosniff",
});
send(state, "channel.hello", {
status: "ready",
transport: "http2-mtls",
trustGeneration: trust.generationRef,
commandTransport: config.commandTransport,
}, {
trackerSessionId: CHANNEL_TRACKER_SESSION_ID,
adapterProfileRef: CHANNEL_PROFILE_REF,
correlationId: `correlation:${randomUUID()}`,
});
let processing = Promise.resolve();
stream.on("data", (chunk) => {
stream.pause();
processing = processing
.then(async () => {
const envelopes = state.decoder.push(chunk);
for (const envelope of envelopes) {
await handleCoreEnvelope(state, envelope);
}
})
.catch(() => protocolFailure(state))
.finally(() => {
if (!state.closed) stream.resume();
});
});
stream.on("aborted", () => closeActive(state));
stream.on("close", () => closeActive(state));
stream.on("error", () => closeActive(state));
});
server.on("tlsClientError", () => {
totalChannelsRejected += 1;
});
server.on("sessionError", () => {
totalProtocolFailures += 1;
});
return Object.freeze({
async start() {
if (started) return server.address();
await listen(server, config.port, config.host);
started = true;
heartbeatTimer = setInterval(checkChannelHealth, config.keepaliveMs);
heartbeatTimer.unref?.();
return server.address();
},
async stop() {
clearInterval(heartbeatTimer);
heartbeatTimer = null;
const current = active;
if (current) {
current.stream.close();
closeActive(current);
}
rejectAllPending("device_edge_channel_stopped");
if (started) await closeServer(server);
started = false;
},
async submitDiscovery(signal) {
const normalized = normalizeDiscoverySignal(signal);
const result = await submitEvent("discovery.observed", {
signal: normalized,
}, {
trackerSessionId: normalized.sessionRef,
adapterProfileRef: normalized.modelProfileRef,
eventAt: normalized.observedAt,
});
if (!result?.discovery) {
throw new Error("device_edge_channel_discovery_acceptance_invalid");
}
return Object.freeze({
...result.discovery,
...(result.commandOffer ? { commandOffer: result.commandOffer } : {}),
});
},
async submitAdapterMessage(message) {
const normalized = normalizeAdapterMessage(message, {
maxBytes: config.maxEnvelopeBytes,
});
const result = await submitEvent("adapter.message", {
message: normalized,
}, {
trackerSessionId: normalized.sessionRef,
adapterProfileRef: normalized.protocolProfileRef,
eventAt: normalized.observedAt,
});
const acceptance = normalizeAdapterAcceptance(result?.acceptance);
if (acceptance.idempotencyKey !== normalized.idempotencyKey) {
throw new Error("device_edge_channel_acceptance_mismatch");
}
return Object.freeze({
...acceptance,
...(result.commandOffer ? { commandOffer: result.commandOffer } : {}),
});
},
async submitCommandStatus(status) {
const normalized = normalizeCommandStatus(status);
const result = await submitEvent("command.status", {
status: normalized,
}, {
trackerSessionId: normalized.sessionRef,
adapterProfileRef: normalized.adapterProfileRef,
eventAt: normalized.observedAt,
});
if (result?.status !== "recorded") {
throw new Error("device_edge_channel_command_status_invalid");
}
return Object.freeze({ status: "recorded" });
},
status() {
return Object.freeze({
listening: started,
channel: active?.accepted ? "accepted" : active ? "negotiating" : "absent",
edgeRegistrationId: config.edgeRegistrationId,
channelGeneration: config.channelGeneration,
trustGeneration: trust.generationRef,
edgeCertificateFingerprint: trust.certificateFingerprint,
pendingAcceptances: pending.size,
channelsAccepted: totalChannelsAccepted,
channelsRejected: totalChannelsRejected,
eventsSubmitted: totalEventsSubmitted,
eventsAccepted: totalEventsAccepted,
eventsRejected: totalEventsRejected,
protocolFailures: totalProtocolFailures,
trackerIngress: "disabled",
commandTransport: config.commandTransport,
});
},
rotateTrust(next) {
const nextTrust = normalizeTrust(next);
if (nextTrust.generationRef === trust.generationRef) {
throw new TypeError("device_edge_channel_trust_generation_unchanged");
}
server.setSecureContext({
key: nextTrust.key,
cert: nextTrust.cert,
ca: nextTrust.ca,
minVersion: "TLSv1.3",
maxVersion: "TLSv1.3",
});
trust = nextTrust;
const current = active;
if (current) {
current.stream.close();
closeActive(current);
}
return Object.freeze({
trustGeneration: trust.generationRef,
edgeCertificateFingerprint: trust.certificateFingerprint,
channel: "reconnect-required",
});
},
disconnectActiveChannel() {
active?.stream.close();
},
});
async function submitEvent(messageKind, payload, metadata) {
const state = active;
if (!state?.accepted || state.closed) {
throw new Error("device_edge_channel_unavailable");
}
if (pending.size >= config.maxPendingAcceptances) {
throw new Error("device_edge_channel_acceptance_window_full");
}
const correlationId = `correlation:${randomUUID()}`;
totalEventsSubmitted += 1;
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
pending.delete(correlationId);
totalEventsRejected += 1;
reject(new Error("device_edge_channel_acceptance_timeout"));
}, config.acceptanceTimeoutMs);
timer.unref?.();
pending.set(correlationId, { resolve, reject, timer });
try {
send(state, messageKind, payload, {
...metadata,
correlationId,
});
} catch (error) {
clearTimeout(timer);
pending.delete(correlationId);
totalEventsRejected += 1;
reject(error);
}
});
}
async function handleCoreEnvelope(state, envelope) {
assertActiveState(state);
if (
envelope.edgeRegistrationId !== config.edgeRegistrationId
|| envelope.channelGeneration !== config.channelGeneration
|| envelope.sequence !== state.coreSequence + 1
) {
throw new Error("device_edge_channel_core_envelope_mismatch");
}
state.coreSequence = envelope.sequence;
state.lastCoreActivityAt = config.clock();
if (!state.accepted) {
if (envelope.messageKind !== "channel.accepted") {
throw new Error("device_edge_channel_acceptance_required");
}
if (
envelope.payload?.status !== "accepted"
|| envelope.payload?.commandTransport !== config.commandTransport
) {
throw new Error("device_edge_channel_acceptance_invalid");
}
state.accepted = true;
totalChannelsAccepted += 1;
return;
}
if (envelope.messageKind === "channel.heartbeat") return;
if (!["event.accepted", "event.rejected"].includes(envelope.messageKind)) {
throw new Error("device_edge_channel_core_message_unhandled");
}
const receipt = pending.get(envelope.correlationId);
if (!receipt) {
throw new Error("device_edge_channel_correlation_unknown");
}
clearTimeout(receipt.timer);
pending.delete(envelope.correlationId);
if (envelope.messageKind === "event.accepted") {
totalEventsAccepted += 1;
receipt.resolve(envelope.payload?.result);
return;
}
totalEventsRejected += 1;
receipt.reject(new Error(normalizeRejectionCode(envelope.payload?.errorCode)));
}
function send(state, messageKind, payload, metadata) {
assertActiveState(state);
state.edgeSequence += 1;
const now = config.now();
const envelope = createChannelEnvelope({
edgeRegistrationId: config.edgeRegistrationId,
channelGeneration: config.channelGeneration,
trackerSessionId: metadata.trackerSessionId,
adapterProfileRef: metadata.adapterProfileRef,
sequence: state.edgeSequence,
eventAt: metadata.eventAt ?? now,
receivedAt: now,
messageKind,
correlationId: metadata.correlationId,
payload,
}, {
direction: "edge-to-core",
maxEnvelopeBytes: config.maxEnvelopeBytes,
});
const encoded = encodeChannelEnvelope(envelope, {
direction: "edge-to-core",
maxEnvelopeBytes: config.maxEnvelopeBytes,
});
if (!state.stream.write(encoded)) {
state.stream.once("drain", () => {});
}
}
function checkChannelHealth() {
const state = active;
if (!state || state.closed) return;
if (config.clock() - state.lastCoreActivityAt >= config.deadPeerMs) {
protocolFailure(state);
return;
}
if (state.accepted) {
try {
send(state, "channel.heartbeat", { status: "alive" }, {
trackerSessionId: CHANNEL_TRACKER_SESSION_ID,
adapterProfileRef: CHANNEL_PROFILE_REF,
correlationId: `correlation:${randomUUID()}`,
});
} catch {
protocolFailure(state);
}
}
}
function isApprovedCorePeer(stream) {
const socket = stream.session?.socket;
if (!socket?.authorized) return false;
let fingerprint;
try {
fingerprint = normalizeCertificateFingerprint(
socket.getPeerCertificate()?.fingerprint256,
);
} catch {
return false;
}
return trust.allowedCoreFingerprints.has(fingerprint);
}
function protocolFailure(state) {
totalProtocolFailures += 1;
state.stream.close();
closeActive(state);
}
function closeActive(state) {
if (state.closed) return;
state.closed = true;
if (active === state) active = null;
rejectAllPending("device_edge_channel_disconnected");
}
function rejectAllPending(code) {
for (const receipt of pending.values()) {
clearTimeout(receipt.timer);
receipt.reject(new Error(code));
}
pending.clear();
}
function assertActiveState(state) {
if (!state || state.closed || active !== state) {
throw new Error("device_edge_channel_unavailable");
}
}
}
function normalizeConfig(options) {
const edgeRegistrationId = normalizeRef(
options.edgeRegistrationId,
"edge_registration_id",
);
const channelGeneration = normalizeRef(
options.channelGeneration,
"channel_generation",
);
const trust = normalizeTrust({
...options.tls,
generationRef: options.trustGeneration,
});
const keepaliveMs = normalizeDuration(options.keepaliveMs, 10,
DEVICE_EDGE_CHANNEL_LIMITS.keepaliveMs, "keepalive");
const deadPeerMs = normalizeDuration(options.deadPeerMs, keepaliveMs * 2,
DEVICE_EDGE_CHANNEL_LIMITS.deadPeerMs, "dead_peer");
if (deadPeerMs < keepaliveMs * 2) {
throw new TypeError("device_edge_channel_dead_peer_invalid");
}
return Object.freeze({
edgeRegistrationId,
channelGeneration,
commandTransport: normalizeCommandTransport(options.commandTransport),
trust,
host: normalizeHost(options.host ?? "127.0.0.1"),
port: normalizePort(options.port ?? 443),
keepaliveMs,
deadPeerMs,
acceptanceTimeoutMs: normalizeDuration(
options.acceptanceTimeoutMs,
10,
5_000,
"acceptance_timeout",
),
maxEnvelopeBytes: normalizeInteger(
options.maxEnvelopeBytes,
256,
DEVICE_EDGE_CHANNEL_LIMITS.maxEnvelopeBytes,
DEVICE_EDGE_CHANNEL_LIMITS.maxEnvelopeBytes,
"max_envelope_bytes",
),
maxPendingAcceptances: normalizeInteger(
options.maxPendingAcceptances,
1,
DEVICE_EDGE_CHANNEL_LIMITS.maxPendingAcceptances,
DEVICE_EDGE_CHANNEL_LIMITS.maxPendingAcceptances,
"max_pending_acceptances",
),
clock: typeof options.clock === "function" ? options.clock : Date.now,
now: typeof options.now === "function"
? () => new Date(options.now()).toISOString()
: () => new Date().toISOString(),
});
}
function normalizeCommandTransport(value) {
const normalized = value ?? "disabled";
if (!["disabled", "typed-service-ping-v1"].includes(normalized)) {
throw new TypeError("device_edge_channel_command_transport_invalid");
}
return normalized;
}
function normalizeCommandStatus(value) {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new TypeError("device_edge_channel_command_status_invalid");
}
const commandRef = normalizeRef(value.commandRef, "command_ref");
const transportMessageRef = normalizeRef(
value.transportMessageRef,
"transport_message_ref",
);
const sessionRef = normalizeRef(value.sessionRef, "tracker_session_ref");
const adapterProfileRef = normalizeRef(
value.adapterProfileRef,
"adapter_profile_ref",
);
if (!["acknowledged", "unknown"].includes(value.lifecycleState)) {
throw new TypeError("device_edge_channel_command_lifecycle_invalid");
}
const resultCode = String(value.resultCode || "");
if (!/^[a-z][a-z0-9._-]{1,63}$/.test(resultCode)) {
throw new TypeError("device_edge_channel_command_result_invalid");
}
const observedAt = new Date(value.observedAt);
if (Number.isNaN(observedAt.getTime())) {
throw new TypeError("device_edge_channel_command_observed_at_invalid");
}
return Object.freeze({
commandRef,
transportMessageRef,
sessionRef,
adapterProfileRef,
lifecycleState: value.lifecycleState,
resultCode,
observedAt: observedAt.toISOString(),
});
}
function normalizeTrust(value) {
if (!value || typeof value !== "object") {
throw new TypeError("device_edge_channel_tls_invalid");
}
for (const key of ["key", "cert", "ca"]) {
if (!(typeof value[key] === "string" || Buffer.isBuffer(value[key]))) {
throw new TypeError(`device_edge_channel_tls_${key}_invalid`);
}
}
if (
!Array.isArray(value.allowedCoreFingerprints)
|| value.allowedCoreFingerprints.length < 1
|| value.allowedCoreFingerprints.length > 2
) {
throw new TypeError("device_edge_channel_core_identity_allowlist_invalid");
}
const generationRef = normalizeRef(
value.generationRef,
"trust_generation",
);
let certificateFingerprint;
try {
certificateFingerprint = normalizeCertificateFingerprint(
new X509Certificate(value.cert).fingerprint256,
);
} catch {
throw new TypeError("device_edge_channel_tls_cert_invalid");
}
const allowedCoreFingerprints = new Set(
value.allowedCoreFingerprints.map(normalizeCertificateFingerprint),
);
if (allowedCoreFingerprints.size !== value.allowedCoreFingerprints.length) {
throw new TypeError("device_edge_channel_core_identity_allowlist_duplicate");
}
return Object.freeze({
generationRef,
key: value.key,
cert: value.cert,
ca: value.ca,
certificateFingerprint,
allowedCoreFingerprints,
});
}
function normalizeRejectionCode(value) {
if (typeof value !== "string" || !/^[a-z][a-z0-9._:-]{2,127}$/.test(value)) {
return "device_edge_channel_event_rejected";
}
return value;
}
function normalizeRef(value, field) {
if (typeof value !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(value)) {
throw new TypeError(`device_edge_channel_${field}_invalid`);
}
return value;
}
function normalizeHost(value) {
if (typeof value !== "string" || value.length < 1 || value.length > 253) {
throw new TypeError("device_edge_channel_host_invalid");
}
return value;
}
function normalizePort(value) {
const number = Number(value);
if (!Number.isSafeInteger(number) || number < 0 || number > 65_535) {
throw new TypeError("device_edge_channel_port_invalid");
}
return number;
}
function normalizeDuration(value, minimum, fallback, field) {
return normalizeInteger(value, minimum, 120_000, fallback, field);
}
function normalizeInteger(value, minimum, maximum, fallback, field) {
const number = value == null ? fallback : Number(value);
if (!Number.isSafeInteger(number) || number < minimum || number > maximum) {
throw new TypeError(`device_edge_channel_${field}_invalid`);
}
return number;
}
function listen(server, port, host) {
return new Promise((resolve, reject) => {
const onError = (error) => {
server.off("listening", onListening);
reject(error);
};
const onListening = () => {
server.off("error", onError);
resolve();
};
server.once("error", onError);
server.once("listening", onListening);
server.listen(port, host);
});
}
function closeServer(server) {
return new Promise((resolve) => server.close(() => resolve()));
}
+203
View File
@@ -0,0 +1,203 @@
import { lstat, readFile } from "node:fs/promises";
import { createServer } from "node:http";
import { pathToFileURL } from "node:url";
import { createDeviceEdgeChannelServer } from "./runtime.mjs";
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
await main();
}
async function main() {
const runtime = await readRuntimeConfiguration(process.env);
const channel = createDeviceEdgeChannelServer(runtime.channel);
const health = createHealthServer(channel, runtime.health);
let stopping = false;
await channel.start();
await listen(health, runtime.health.port, runtime.health.host);
console.log(JSON.stringify({
event: "device_edge_channel_started",
host: runtime.channel.host,
port: runtime.channel.port,
healthHost: runtime.health.host,
healthPort: runtime.health.port,
edgeRegistrationId: runtime.channel.edgeRegistrationId,
channelGeneration: runtime.channel.channelGeneration,
trustGeneration: runtime.channel.trustGeneration,
trackerIngress: "disabled",
commandTransport: "disabled",
}));
process.on("SIGTERM", shutdown);
process.on("SIGINT", shutdown);
async function shutdown() {
if (stopping) return;
stopping = true;
await Promise.allSettled([
channel.stop(),
closeServer(health),
]);
process.exit(0);
}
}
export async function readRuntimeConfiguration(environment = {}) {
const configPath = requiredPath(
environment.DEVICE_EDGE_CHANNEL_CONFIG_FILE,
"device_edge_channel_config_file_required",
);
const keyPath = requiredPath(
environment.DEVICE_EDGE_CHANNEL_KEY_FILE,
"device_edge_channel_key_file_required",
);
const certificatePath = requiredPath(
environment.DEVICE_EDGE_CHANNEL_CERTIFICATE_FILE,
"device_edge_channel_certificate_file_required",
);
const coreTrustPath = requiredPath(
environment.DEVICE_EDGE_CHANNEL_CORE_TRUST_FILE,
"device_edge_channel_core_trust_file_required",
);
const config = normalizeRuntimeDocument(JSON.parse(
await readBoundedRegularFile(configPath, 32 * 1024, "utf8"),
));
const [key, cert, ca] = await Promise.all([
readBoundedRegularFile(keyPath, 32 * 1024),
readBoundedRegularFile(certificatePath, 32 * 1024),
readBoundedRegularFile(coreTrustPath, 64 * 1024),
]);
return Object.freeze({
channel: Object.freeze({
edgeRegistrationId: config.edgeRegistrationId,
channelGeneration: config.channelGeneration,
trustGeneration: config.trustGeneration,
host: normalizeHost(environment.DEVICE_EDGE_CHANNEL_HOST ?? "0.0.0.0"),
port: normalizePort(environment.DEVICE_EDGE_CHANNEL_PORT, 443),
tls: Object.freeze({
key,
cert,
ca,
allowedCoreFingerprints: config.allowedCoreFingerprints,
}),
}),
health: Object.freeze({
host: normalizeHost(
environment.DEVICE_EDGE_CHANNEL_HEALTH_HOST ?? "127.0.0.1",
),
port: normalizePort(environment.DEVICE_EDGE_CHANNEL_HEALTH_PORT, 18222),
}),
});
}
export function normalizeRuntimeDocument(value) {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new TypeError("device_edge_channel_runtime_config_invalid");
}
const allowedKeys = new Set([
"schemaVersion",
"edgeRegistrationId",
"channelGeneration",
"trustGeneration",
"allowedCoreFingerprints",
]);
if (Object.keys(value).some((key) => !allowedKeys.has(key))) {
throw new TypeError("device_edge_channel_runtime_config_key_invalid");
}
if (value.schemaVersion !== "nodedc.device-edge.channel-runtime.v1") {
throw new TypeError("device_edge_channel_runtime_schema_invalid");
}
const fingerprints = value.allowedCoreFingerprints;
if (!Array.isArray(fingerprints) || fingerprints.length < 1 || fingerprints.length > 2) {
throw new TypeError("device_edge_channel_runtime_core_identity_invalid");
}
if (new Set(fingerprints).size !== fingerprints.length) {
throw new TypeError("device_edge_channel_runtime_core_identity_invalid");
}
return Object.freeze({
schemaVersion: value.schemaVersion,
edgeRegistrationId: normalizeRef(value.edgeRegistrationId, "edge_registration"),
channelGeneration: normalizeRef(value.channelGeneration, "channel_generation"),
trustGeneration: normalizeRef(value.trustGeneration, "trust_generation"),
allowedCoreFingerprints: Object.freeze(fingerprints.map((fingerprint) => {
if (typeof fingerprint !== "string" || !/^([A-F0-9]{2}:){31}[A-F0-9]{2}$/.test(fingerprint)) {
throw new TypeError("device_edge_channel_runtime_core_identity_invalid");
}
return fingerprint;
})),
});
}
function createHealthServer(channel, healthConfig) {
return createServer((request, response) => {
response.setHeader("Content-Type", "application/json; charset=utf-8");
response.setHeader("Cache-Control", "no-store");
response.setHeader("X-Content-Type-Options", "nosniff");
if (request.method !== "GET" || request.url !== "/healthz") {
response.statusCode = 404;
response.end(JSON.stringify({ ok: false, error: "not_found" }));
return;
}
const status = channel.status();
response.statusCode = 200;
response.end(JSON.stringify({
ok: true,
service: "nodedc-device-edge-channel",
health: `${healthConfig.host}:${healthConfig.port}`,
...status,
}));
});
}
async function readBoundedRegularFile(path, maximumBytes, encoding = null) {
const state = await lstat(path);
if (!state.isFile() || state.isSymbolicLink() || state.size < 1 || state.size > maximumBytes) {
throw new Error("device_edge_channel_runtime_file_invalid");
}
return readFile(path, encoding ?? undefined);
}
function requiredPath(value, code) {
if (typeof value !== "string" || value.trim() === "" || !value.startsWith("/")) {
throw new Error(code);
}
return value;
}
function normalizeRef(value, field) {
if (typeof value !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(value)) {
throw new TypeError(`device_edge_channel_runtime_${field}_invalid`);
}
return value;
}
function normalizeHost(value) {
if (typeof value !== "string" || value.length < 1 || value.length > 253) {
throw new TypeError("device_edge_channel_runtime_host_invalid");
}
return value;
}
function normalizePort(value, fallback) {
const number = Number(value ?? fallback);
if (!Number.isSafeInteger(number) || number < 1 || number > 65_535) {
throw new TypeError("device_edge_channel_runtime_port_invalid");
}
return number;
}
function listen(server, port, host) {
return new Promise((resolve, reject) => {
server.once("error", reject);
server.listen(port, host, () => {
server.off("error", reject);
resolve();
});
});
}
function closeServer(server) {
return new Promise((resolve) => server.close(() => resolve()));
}
@@ -0,0 +1,765 @@
import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import { randomUUID, X509Certificate } from "node:crypto";
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { connect as connectHttp2 } from "node:http2";
import { createServer as createTcpServer } from "node:net";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { after, before, test } from "node:test";
import {
DEVICE_ADAPTER_ACCEPTANCE_SCHEMA,
DEVICE_ADAPTER_MESSAGE_SCHEMA,
DEVICE_DISCOVERY_SIGNAL_SCHEMA,
} from "../../../packages/device-protocol-contract/src/index.mjs";
import { createDeviceGatewayCoreChannelClient } from
"../../device-gateway-core/src/runtime.mjs";
import { createDeviceGatewayIngest } from
"../../device-control-core/src/gateway-ingest.mjs";
import { createDeviceEdgeChannelServer } from "../src/runtime.mjs";
let fixtureDirectory;
let certificates;
before(async () => {
fixtureDirectory = await mkdtemp(join(tmpdir(), "nodedc-edge-channel-"));
certificates = await generateCertificateFixture(fixtureDirectory);
});
after(async () => {
await rm(fixtureDirectory, { recursive: true, force: true });
});
test("accepts synthetic discovery and durable message results over Core-initiated mTLS", async () => {
const acceptedMessages = new Map();
let messageCalls = 0;
const pair = await startPair({
observeDiscovery: productionDiscoveryObserver(),
acceptMessage: async (message) => {
messageCalls += 1;
const previous = acceptedMessages.get(message.idempotencyKey);
if (previous) return { ...previous, replayed: true };
const acceptance = acceptanceFor(message, false);
acceptedMessages.set(message.idempotencyKey, acceptance);
return acceptance;
},
});
try {
const discovery = await pair.edge.submitDiscovery(discoverySignal());
assert.equal(discovery.lifecycleState, "quarantine");
assert.equal(discovery.identifier.masked, "***********0001");
assert.equal("created" in discovery, false);
assert.equal("value" in discovery, false);
const message = adapterMessage();
const first = await pair.edge.submitAdapterMessage(message);
const replay = await pair.edge.submitAdapterMessage(message);
assert.equal(first.replayed, false);
assert.equal(replay.replayed, true);
assert.equal(messageCalls, 2);
assert.equal(pair.edge.status().eventsAccepted, 3);
assert.equal(pair.core.status().eventsAccepted, 3);
assert.equal(pair.edge.status().trackerIngress, "disabled");
assert.equal(pair.core.status().commandTransport, "disabled");
} finally {
await stopPair(pair);
}
});
test("recovers the claimed device from telemetry after a Core restart and completes a typed command", async () => {
const commandRef = "command:11111111-1111-4111-8111-111111111111";
const deviceRef = "device:22222222-2222-4222-8222-222222222222";
const transportMessageRef = "edge-command:33333333-3333-4333-8333-333333333333";
const recorded = [];
let offers = 0;
const pair = await startPair({
commandTransport: "typed-service-ping-v1",
acceptMessage: async (message) => ({
value: acceptanceFor(message, false),
claimedDeviceRef: deviceRef,
}),
offerCommand: async (offeredDeviceRef) => {
assert.equal(offeredDeviceRef, deviceRef);
offers += 1;
return {
commandRef,
commandType: "service.ping",
accessCode: "123456",
transportMessageRef,
};
},
recordCommandStatus: async (status) => recorded.push(status),
});
try {
const receipt = await pair.edge.submitAdapterMessage(adapterMessage());
assert.equal(receipt.status, "accepted");
assert.deepEqual(receipt.commandOffer, {
commandRef,
commandType: "service.ping",
accessCode: "123456",
transportMessageRef,
});
assert.equal(offers, 1);
await pair.edge.submitCommandStatus({
commandRef,
transportMessageRef,
lifecycleState: "acknowledged",
resultCode: "serv_ok",
observedAt: "2026-08-12T17:30:00.000Z",
sessionRef: "session:pilot-1",
adapterProfileRef: "arusnavi.internal.b2.v1",
});
assert.equal(recorded.length, 1);
assert.equal(recorded[0].resultCode, "serv_ok");
assert.equal(pair.core.status().negotiatedCommandTransport, "typed-service-ping-v1");
} finally {
await stopPair(pair);
}
});
test("keeps the channel alive and reconnects without losing idempotency", async () => {
const acceptedMessages = new Map();
const pair = await startPair({
keepaliveMs: 20,
deadPeerMs: 60,
reconnectMinimumMs: 20,
reconnectMaximumMs: 40,
acceptMessage: async (message) => {
const previous = acceptedMessages.get(message.idempotencyKey);
if (previous) return { ...previous, replayed: true };
const acceptance = acceptanceFor(message, false);
acceptedMessages.set(message.idempotencyKey, acceptance);
return acceptance;
},
});
try {
await delay(120);
assert.equal(pair.edge.status().channel, "accepted");
const message = adapterMessage();
assert.equal((await pair.edge.submitAdapterMessage(message)).replayed, false);
pair.edge.disconnectActiveChannel();
await waitFor(() => pair.core.status().connectionAttempts >= 2
&& pair.edge.status().channel === "accepted", 2_000);
assert.equal((await pair.edge.submitAdapterMessage(message)).replayed, true);
assert.ok(pair.core.status().reconnects >= 1);
} finally {
await stopPair(pair);
}
});
test("bounds a stalled TCP/TLS handshake and reconnects", async () => {
const sockets = new Set();
const server = createTcpServer((socket) => {
sockets.add(socket);
socket.once("close", () => sockets.delete(socket));
});
await new Promise((resolve, reject) => {
server.once("error", reject);
server.listen(0, "127.0.0.1", resolve);
});
const core = createCoreClient({
address: server.address(),
connectTimeoutMs: 30,
reconnectMinimumMs: 20,
reconnectMaximumMs: 20,
});
try {
await core.start();
await waitFor(() => core.status().connectionAttempts >= 2, 1_000);
assert.match(core.status().lastErrorCode, /connect_timeout/);
assert.ok(core.status().reconnects >= 1);
} finally {
await core.stop();
for (const socket of sockets) socket.destroy();
await new Promise((resolve) => server.close(resolve));
}
});
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();
const core = createCoreClient({
address,
lifecycleState: "revoked",
});
try {
await core.start();
await waitFor(() => core.status().connectionAttempts >= 1, 500);
assert.equal(core.status().channel, "absent");
assert.equal(edge.status().channelsAccepted, 0);
assert.match(core.status().lastErrorCode, /registration_inactive/);
} finally {
await core.stop();
await edge.stop();
}
});
test("rejects an Edge hello from an unexpected channel generation", async () => {
const edge = createEdgeServer({
channelGeneration: "generation:edge-unexpected",
});
const address = await edge.start();
const core = createCoreClient({ address });
try {
await core.start();
await waitFor(() => core.status().protocolFailures >= 1, 2_000);
assert.notEqual(core.status().channel, "accepted");
assert.match(core.status().lastErrorCode, /channel_generation_mismatch/);
assert.equal(edge.status().channelsAccepted, 0);
} finally {
await core.stop();
await edge.stop();
}
});
test("rejects an authenticated but non-allowlisted Core certificate", async () => {
const edge = createEdgeServer();
const address = await edge.start();
const core = createCoreClient({
address,
clientCertificate: certificates.intruder,
});
try {
await core.start();
await waitFor(() => edge.status().channelsRejected >= 1, 2_000);
assert.equal(edge.status().channelsAccepted, 0);
assert.equal(edge.status().channel, "absent");
} finally {
await core.stop();
await edge.stop();
}
});
test("rejects an unknown Edge certificate fingerprint", async () => {
const edge = createEdgeServer();
const address = await edge.start();
const core = createCoreClient({
address,
expectedEdgeFingerprint: certificates.intruder.fingerprint,
});
try {
await core.start();
await waitFor(() => core.status().protocolFailures >= 1, 2_000);
assert.equal(core.status().channelsAccepted, 0);
assert.match(core.status().lastErrorCode, /edge_identity_mismatch/);
} finally {
await core.stop();
await edge.stop();
}
});
test("returns a conclusive rejection when Core cannot accept a package", async () => {
const pair = await startPair({
acceptMessage: async () => {
throw new Error("device_control_core_unavailable");
},
});
try {
await assert.rejects(
pair.edge.submitAdapterMessage(adapterMessage()),
/device_control_core_unavailable/,
);
assert.equal(pair.edge.status().eventsRejected, 1);
assert.equal(pair.core.status().eventsRejected, 1);
} finally {
await stopPair(pair);
}
});
test("rejects a discovery when Core does not return its durable receipt", async () => {
const pair = await startPair({
observeDiscovery: async () => ({
schemaVersion: "nodedc.device.discovery-view.v1",
lifecycleState: "quarantine",
commandTransport: "disabled",
identifier: { kind: "imei", masked: "***********1088" },
}),
});
try {
await assert.rejects(
pair.edge.submitDiscovery(discoverySignal()),
/device_gateway_core_discovery_receipt_invalid/,
);
assert.equal(pair.edge.status().eventsRejected, 1);
assert.equal(pair.core.status().eventsRejected, 1);
} finally {
await stopPair(pair);
}
});
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();
const session = connectHttp2(`https://127.0.0.1:${address.port}`, {
key: certificates.core.key,
cert: certificates.core.cert,
ca: certificates.ca,
servername: "localhost",
minVersion: "TLSv1.3",
maxVersion: "TLSv1.3",
rejectUnauthorized: true,
});
try {
await onceEvent(session, "connect");
const request = session.request({
":method": "POST",
":path": "/internal/v1/device-edge/channel",
}, { endStream: false });
const hello = await readFirstEnvelope(request);
const invalid = {
schemaVersion: hello.schemaVersion,
edgeRegistrationId: hello.edgeRegistrationId,
channelGeneration: hello.channelGeneration,
trackerSessionId: "channel:control",
adapterProfileRef: "channel.control.v1",
sequence: 1,
eventAt: new Date().toISOString(),
receivedAt: new Date().toISOString(),
payloadBytes: 2,
messageKind: "tcp.forward",
correlationId: `correlation:${randomUUID()}`,
payload: {},
};
request.write(`${JSON.stringify(invalid)}\n`);
await waitFor(() => edge.status().protocolFailures >= 1, 1_000);
assert.equal(edge.status().channel, "absent");
request.close();
} finally {
session.close();
await edge.stop();
}
});
async function startPair(options = {}) {
const edge = createEdgeServer(options);
const address = await edge.start();
const core = createCoreClient({ address, ...options });
await core.start();
await core.waitForReady(2_000);
await waitFor(() => edge.status().channel === "accepted", 2_000);
return { edge, core };
}
function createEdgeServer(options = {}) {
const edgeCertificate = options.edgeCertificate ?? certificates.edge;
return createDeviceEdgeChannelServer({
edgeRegistrationId: "edge:pilot-1",
channelGeneration: options.channelGeneration ?? "generation:pilot-1",
trustGeneration: options.edgeTrustGeneration ?? "trust-generation:1",
host: "127.0.0.1",
port: 0,
tls: {
key: edgeCertificate.key,
cert: edgeCertificate.cert,
ca: certificates.ca,
allowedCoreFingerprints: options.allowedCoreFingerprints
?? [certificates.core.fingerprint],
},
keepaliveMs: options.keepaliveMs ?? 50,
deadPeerMs: options.deadPeerMs ?? 150,
acceptanceTimeoutMs: 500,
maxPendingAcceptances: options.maxPendingAcceptances,
commandTransport: options.commandTransport,
});
}
function createCoreClient(options) {
const clientCertificate = options.clientCertificate ?? certificates.core;
const registration = edgeRegistration(options.address,
options.edgeCertificateIdentities ?? [{
generationRef: "trust-generation:1",
fingerprint: options.expectedEdgeFingerprint
?? certificates.edge.fingerprint,
status: "active",
}],
options.lifecycleState ?? "active");
return createDeviceGatewayCoreChannelClient({
...(options.registrationProvider
? { registrationProvider: options.registrationProvider }
: { registration }),
tls: {
key: clientCertificate.key,
cert: clientCertificate.cert,
ca: certificates.ca,
},
coreIdentity: "workload:device-gateway-core",
keepaliveMs: options.keepaliveMs ?? 50,
deadPeerMs: options.deadPeerMs ?? 150,
reconnectMinimumMs: options.reconnectMinimumMs ?? 20,
reconnectMaximumMs: options.reconnectMaximumMs ?? 80,
connectTimeoutMs: options.connectTimeoutMs,
random: () => 0,
observeDiscovery: options.observeDiscovery ?? productionDiscoveryObserver(),
acceptMessage: options.acceptMessage ?? (async (message) =>
acceptanceFor(message, false)),
commandTransport: options.commandTransport,
offerCommand: options.offerCommand,
recordCommandStatus: options.recordCommandStatus,
});
}
function productionDiscoveryObserver() {
return createDeviceGatewayIngest({
identifierPepper: "test-only-device-edge-channel-identifier-pepper",
repository: {
async upsertQuarantineDiscovery(value) {
return {
created: true,
value: {
...value.safeView,
discoveryRef: "discovery:pilot-1",
},
};
},
async acceptAdapterMessage() {
throw new Error("device_edge_channel_test_unexpected_ingest_message");
},
},
}).observeDiscovery;
}
function edgeRegistration(address, certificateIdentities, lifecycleState = "active") {
return {
edgeRegistrationId: "edge:pilot-1",
channelGeneration: "generation: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();
}
function discoverySignal() {
return {
schemaVersion: DEVICE_DISCOVERY_SIGNAL_SCHEMA,
sessionRef: "session:pilot-1",
modelProfileRef: "arusnavi.internal.b2.v1",
protocol: "INTERNAL",
observedAt: new Date().toISOString(),
identifier: { kind: "imei", value: "860000000000001" },
evidence: {
transport: "tcp",
bytesObserved: 10,
framingStatus: "verified",
specificationRef: "arusnavi.internal.protocol.v1",
},
};
}
function adapterMessage(overrides = {}) {
return {
schemaVersion: DEVICE_ADAPTER_MESSAGE_SCHEMA,
edgeRef: "edge:pilot-1",
adapterRef: "arusnavi-b2",
protocolProfileRef: "arusnavi.internal.b2.v1",
protocol: "INTERNAL",
sessionRef: "session:pilot-1",
messageRef: "message:pilot-1",
messageType: "telemetry.package",
sequence: 1,
observedAt: new Date().toISOString(),
idempotencyKey: `sha256:${"a".repeat(64)}`,
identifier: { kind: "imei", value: "860000000000001" },
payloadSchemaRef: "arusnavi.internal.package-metadata.v1",
payload: {
packageNumber: 1,
packetCount: 1,
byteLength: 11,
packageDigest: `sha256:${"b".repeat(64)}`,
},
...overrides,
};
}
function acceptanceFor(message, replayed) {
return {
schemaVersion: DEVICE_ADAPTER_ACCEPTANCE_SCHEMA,
acceptanceRef: "acceptance:pilot-1",
idempotencyKey: message.idempotencyKey,
status: "accepted",
replayed,
acceptedAt: "2026-08-11T12:00:00.000Z",
};
}
async function generateCertificateFixture(directory) {
runOpenSsl(directory, [
"req", "-x509", "-newkey", "rsa:2048", "-nodes", "-sha256",
"-days", "1", "-subj", "/CN=NODEDC Test Device Edge CA",
"-addext", "basicConstraints=critical,CA:TRUE",
"-addext", "keyUsage=critical,keyCertSign,cRLSign",
"-keyout", "ca.key", "-out", "ca.crt",
]);
await issueCertificate(directory, "edge", "localhost", [
"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",
]);
await issueCertificate(directory, "intruder", "unapproved-core", [
"extendedKeyUsage=clientAuth",
]);
const ca = await readFile(join(directory, "ca.crt"));
return {
ca,
edge: await readCertificate(directory, "edge"),
edgeNext: await readCertificate(directory, "edge-next"),
core: await readCertificate(directory, "core"),
intruder: await readCertificate(directory, "intruder"),
};
}
async function issueCertificate(directory, name, commonName, extensions) {
runOpenSsl(directory, [
"req", "-newkey", "rsa:2048", "-nodes", "-sha256",
"-subj", `/CN=${commonName}`,
"-keyout", `${name}.key`, "-out", `${name}.csr`,
]);
const extensionFile = `${name}.ext`;
await writeFile(join(directory, extensionFile), [
"basicConstraints=critical,CA:FALSE",
"keyUsage=critical,digitalSignature,keyEncipherment",
...extensions,
].join("\n"));
runOpenSsl(directory, [
"x509", "-req", "-sha256", "-days", "1",
"-in", `${name}.csr`, "-CA", "ca.crt", "-CAkey", "ca.key",
"-CAcreateserial", "-extfile", extensionFile, "-out", `${name}.crt`,
]);
}
async function readCertificate(directory, name) {
const cert = await readFile(join(directory, `${name}.crt`));
return {
key: await readFile(join(directory, `${name}.key`)),
cert,
fingerprint: new X509Certificate(cert).fingerprint256,
};
}
function runOpenSsl(directory, arguments_) {
const result = spawnSync("openssl", arguments_, {
cwd: directory,
encoding: "utf8",
});
if (result.status !== 0) {
throw new Error(`openssl_failed:${result.stderr}`);
}
}
function readFirstEnvelope(stream) {
return new Promise((resolve, reject) => {
let buffered = "";
const onData = (chunk) => {
buffered += chunk.toString("utf8");
const newline = buffered.indexOf("\n");
if (newline < 0) return;
cleanup();
resolve(JSON.parse(buffered.slice(0, newline)));
};
const onError = (error) => {
cleanup();
reject(error);
};
const cleanup = () => {
stream.off("data", onData);
stream.off("error", onError);
};
stream.on("data", onData);
stream.once("error", onError);
});
}
function onceEvent(emitter, event) {
return new Promise((resolve, reject) => {
const onEvent = (...args) => {
cleanup();
resolve(args);
};
const onError = (error) => {
cleanup();
reject(error);
};
const cleanup = () => {
emitter.off(event, onEvent);
emitter.off("error", onError);
};
emitter.once(event, onEvent);
emitter.once("error", onError);
});
}
async function waitFor(predicate, timeoutMs) {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
if (predicate()) return;
await delay(10);
}
assert.fail("condition_timeout");
}
function delay(milliseconds) {
return new Promise((resolve) => setTimeout(resolve, milliseconds));
}
@@ -0,0 +1,46 @@
import assert from "node:assert/strict";
import { test } from "node:test";
import { normalizeRuntimeDocument } from "../src/server.mjs";
const fingerprint = Array.from({ length: 32 }, () => "AB").join(":");
test("runtime document accepts one generation-bound Core identity", () => {
const result = normalizeRuntimeDocument({
schemaVersion: "nodedc.device-edge.channel-runtime.v1",
edgeRegistrationId: "edge:moscow-vps-1",
channelGeneration: "channel:1",
trustGeneration: "trust:1",
allowedCoreFingerprints: [fingerprint],
});
assert.equal(result.edgeRegistrationId, "edge:moscow-vps-1");
assert.deepEqual(result.allowedCoreFingerprints, [fingerprint]);
});
test("runtime document rejects hidden authority and missing identity", () => {
assert.throws(() => normalizeRuntimeDocument({
schemaVersion: "nodedc.device-edge.channel-runtime.v1",
edgeRegistrationId: "edge:moscow-vps-1",
channelGeneration: "channel:1",
trustGeneration: "trust:1",
allowedCoreFingerprints: [fingerprint],
endpoint: "https://attacker.invalid",
}), /runtime_config_key_invalid/);
assert.throws(() => normalizeRuntimeDocument({
schemaVersion: "nodedc.device-edge.channel-runtime.v1",
edgeRegistrationId: "edge:moscow-vps-1",
channelGeneration: "channel:1",
trustGeneration: "trust:1",
allowedCoreFingerprints: [],
}), /runtime_core_identity_invalid/);
assert.throws(() => normalizeRuntimeDocument({
schemaVersion: "nodedc.device-edge.channel-runtime.v1",
edgeRegistrationId: "edge:moscow-vps-1",
channelGeneration: "channel:1",
trustGeneration: "trust:1",
allowedCoreFingerprints: [fingerprint, fingerprint],
}), /runtime_core_identity_invalid/);
});
@@ -0,0 +1,32 @@
import assert from "node:assert/strict";
import { test } from "node:test";
import {
normalizeTrackerIngressConfiguration,
} from "../../../vps/edge-process/device-edge-runtime.mjs";
test("VPS tracker ingress resolves one allowlisted profile within bounded limits", () => {
const config = normalizeTrackerIngressConfiguration({}, "edge:moscow-vps-1");
assert.equal(config.protocolProfileRef, "arusnavi.b2.internal.v1");
assert.equal(config.tcpHost, "0.0.0.0");
assert.equal(config.tcpPort, 9921);
assert.equal(config.maxConcurrentSessions, 128);
assert.equal(config.maxSessionsPerAddress, 16);
assert.equal(config.maxConnectionsPerMinutePerAddress, 60);
assert.equal(config.maxAggregateBufferedBytes, 32 * 1024 * 1024);
});
test("VPS tracker ingress rejects hidden bind and non-allowlisted adapters", () => {
assert.throws(() => normalizeTrackerIngressConfiguration({
DEVICE_GATEWAY_TCP_HOST: "127.0.0.1",
}, "edge:moscow-vps-1"), /public_host_invalid/);
assert.throws(() => normalizeTrackerIngressConfiguration({
DEVICE_GATEWAY_PROTOCOL_PROFILE_REF: "vendor.unknown.v1",
}, "edge:moscow-vps-1"), /profile_not_allowlisted/);
assert.throws(() => normalizeTrackerIngressConfiguration({
DEVICE_GATEWAY_MAX_SESSIONS: "129",
}, "edge:moscow-vps-1"), /session_limit_invalid/);
});