livedesk 0.1.457 → 0.1.459
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/livedesk.js +18 -2
- package/client/package.json +5 -5
- package/diagnostics/livedesk-mac-physical-gate-core.mjs +389 -36
- package/diagnostics/livedesk-mode3-capture-smoke.mjs +323 -21
- package/diagnostics/livedesk-transport-physical-core.mjs +503 -0
- package/diagnostics/livedesk-transport-physical.mjs +508 -0
- package/hub/package.json +1 -1
- package/hub/src/remote-hub.js +841 -40
- package/hub/src/server.js +155 -5
- package/hub/src/transport/udp-hub-transport.js +110 -8
- package/hub/src/transport/udp-rendezvous.js +6 -2
- package/package.json +7 -7
- package/web/dist/assets/index-JqqQ5DKN.js +25 -0
- package/web/dist/index.html +1 -1
- package/web/dist/assets/index-Bv9NFUjC.js +0 -25
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
import { isIP } from 'node:net';
|
|
2
|
+
|
|
3
|
+
const REQUIRED_PHASES = Object.freeze([
|
|
4
|
+
'direct',
|
|
5
|
+
'p2p',
|
|
6
|
+
'relay',
|
|
7
|
+
'recover-direct'
|
|
8
|
+
]);
|
|
9
|
+
|
|
10
|
+
const MAX_PHASE_LATENCY_MS = 15_000;
|
|
11
|
+
const MAX_AGENT_QUEUE_DEPTH = 8;
|
|
12
|
+
const MAX_AGENT_QUEUE_DROPPED_DELTA = 8;
|
|
13
|
+
const MAX_HUB_INGRESS_DROPPED_DELTA = 8;
|
|
14
|
+
const MAX_UDP_INCOMPLETE_DELTA = 2;
|
|
15
|
+
const MAX_HUB_ADDRESS_OBSERVATION_SKEW_MS = 60_000;
|
|
16
|
+
|
|
17
|
+
const SECRET_KEYS = new Set([
|
|
18
|
+
'authorization',
|
|
19
|
+
'bearer',
|
|
20
|
+
'token',
|
|
21
|
+
'secret',
|
|
22
|
+
'password',
|
|
23
|
+
'apikey',
|
|
24
|
+
'pairtoken'
|
|
25
|
+
]);
|
|
26
|
+
const SAFE_NETWORK_KEYS = new Set([
|
|
27
|
+
'clientaddresshash',
|
|
28
|
+
'hubaddresshash',
|
|
29
|
+
'clientaddresspublic',
|
|
30
|
+
'hubaddresspublic',
|
|
31
|
+
'hubaddressobservedat',
|
|
32
|
+
'endpointauthenticated',
|
|
33
|
+
'differentpublicegress',
|
|
34
|
+
'evidence',
|
|
35
|
+
'observedat',
|
|
36
|
+
'udpready'
|
|
37
|
+
]);
|
|
38
|
+
const IDENTIFIER_KEYS = new Set([
|
|
39
|
+
'leaseid',
|
|
40
|
+
'deviceid',
|
|
41
|
+
'selecteddeviceid',
|
|
42
|
+
'streamid',
|
|
43
|
+
'streamcommandid',
|
|
44
|
+
'commandid',
|
|
45
|
+
'sessionid',
|
|
46
|
+
'requestid',
|
|
47
|
+
'connectionid',
|
|
48
|
+
'hubconnectionid',
|
|
49
|
+
'inputeventid',
|
|
50
|
+
'clientid',
|
|
51
|
+
'ownerid'
|
|
52
|
+
]);
|
|
53
|
+
const SECRET_TEXT = /\b(?:authorization|bearer|token|secret|password|api[-_ ]?key|pair[-_ ]?token)(?:\s+|\s*[:=]\s*)\S+/i;
|
|
54
|
+
const IPV4_TEXT = /(^|[^\d])(?:\d{1,3}\.){3}\d{1,3}(?::\d{1,5})?($|[^\d])/;
|
|
55
|
+
const IPV6_CANDIDATE_TEXT = /\[[0-9a-f:.%]+\](?::\d{1,5})?|[0-9a-f:%]*:[0-9a-f:.%:]*/gi;
|
|
56
|
+
const URL_TEXT = /\b[a-z][a-z0-9+.-]*:\/\/[^\s]+/i;
|
|
57
|
+
const HOST_TEXT = /\b(?:localhost|(?:[a-z0-9-]+\.)+[a-z]{2,63})(?::\d{1,5})?\b/i;
|
|
58
|
+
const UUID_TEXT = /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/i;
|
|
59
|
+
|
|
60
|
+
function normalizedKey(key) {
|
|
61
|
+
return String(key || '').replace(/[-_]/g, '').toLowerCase();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function isIdentifierKey(key) {
|
|
65
|
+
return IDENTIFIER_KEYS.has(normalizedKey(key));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function isSecretKey(key) {
|
|
69
|
+
const normalized = normalizedKey(key);
|
|
70
|
+
return SECRET_KEYS.has(normalized)
|
|
71
|
+
|| normalized.endsWith('token')
|
|
72
|
+
|| normalized.endsWith('secret')
|
|
73
|
+
|| normalized.endsWith('password')
|
|
74
|
+
|| normalized.endsWith('apikey');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function isRawNetworkKey(key) {
|
|
78
|
+
const rawKey = String(key || '');
|
|
79
|
+
const normalized = normalizedKey(key);
|
|
80
|
+
if (SAFE_NETWORK_KEYS.has(normalized) || normalized.endsWith('hash')) return false;
|
|
81
|
+
return normalized === 'address'
|
|
82
|
+
|| normalized.endsWith('address')
|
|
83
|
+
|| normalized === 'endpoint'
|
|
84
|
+
|| normalized.endsWith('endpoint')
|
|
85
|
+
|| normalized === 'host'
|
|
86
|
+
|| normalized.endsWith('host')
|
|
87
|
+
|| normalized === 'port'
|
|
88
|
+
|| /(?:^|[-_])port$/i.test(rawKey)
|
|
89
|
+
|| /[A-Z]Port$/.test(rawKey)
|
|
90
|
+
|| normalized === 'url'
|
|
91
|
+
|| normalized.endsWith('url')
|
|
92
|
+
|| normalized === 'uri'
|
|
93
|
+
|| normalized.endsWith('uri')
|
|
94
|
+
|| normalized === 'origin'
|
|
95
|
+
|| normalized.endsWith('origin');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function containsIpv6Text(value) {
|
|
99
|
+
const candidates = String(value || '').match(IPV6_CANDIDATE_TEXT) || [];
|
|
100
|
+
return candidates.some(candidate => {
|
|
101
|
+
const bracketed = candidate.match(/^\[([^\]]+)\](?::\d{1,5})?$/);
|
|
102
|
+
const address = String(bracketed?.[1] || candidate)
|
|
103
|
+
.replace(/%[a-z0-9_.-]+$/i, '')
|
|
104
|
+
.replace(/[.,;]+$/g, '');
|
|
105
|
+
return isIP(address) === 6;
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function containsRawNetworkText(value) {
|
|
110
|
+
const text = String(value || '');
|
|
111
|
+
return IPV4_TEXT.test(text)
|
|
112
|
+
|| containsIpv6Text(text)
|
|
113
|
+
|| URL_TEXT.test(text)
|
|
114
|
+
|| HOST_TEXT.test(text);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function identifierMarker(value) {
|
|
118
|
+
if (value === null || value === undefined || String(value) === '') return '';
|
|
119
|
+
return '[redacted-id]';
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function genericTransportPhysicalError(value, fallback = 'transport-diagnostic-error') {
|
|
123
|
+
const text = String(value instanceof Error ? value.message : value || '').trim();
|
|
124
|
+
if (!text
|
|
125
|
+
|| containsRawNetworkText(text)
|
|
126
|
+
|| SECRET_TEXT.test(text)
|
|
127
|
+
|| UUID_TEXT.test(text)) return fallback;
|
|
128
|
+
if (!/^[a-z0-9][a-z0-9 _.,:;()[\]/'"+=-]{0,239}$/i.test(text)) return fallback;
|
|
129
|
+
return text;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function redactTransportPhysicalReport(value, key = '') {
|
|
133
|
+
if (isSecretKey(key)) return '[redacted]';
|
|
134
|
+
if (isIdentifierKey(key)) return identifierMarker(value);
|
|
135
|
+
if (isRawNetworkKey(key)) return '[redacted-network]';
|
|
136
|
+
if (Array.isArray(value)) {
|
|
137
|
+
return value.map(item => redactTransportPhysicalReport(item));
|
|
138
|
+
}
|
|
139
|
+
if (value && typeof value === 'object') {
|
|
140
|
+
return Object.fromEntries(Object.entries(value)
|
|
141
|
+
.filter(([childKey]) => !isSecretKey(childKey))
|
|
142
|
+
.map(([childKey, childValue]) => [
|
|
143
|
+
childKey,
|
|
144
|
+
redactTransportPhysicalReport(childValue, childKey)
|
|
145
|
+
]));
|
|
146
|
+
}
|
|
147
|
+
if (typeof value === 'string'
|
|
148
|
+
&& (containsRawNetworkText(value)
|
|
149
|
+
|| SECRET_TEXT.test(value)
|
|
150
|
+
|| UUID_TEXT.test(value))) {
|
|
151
|
+
return /error|reason|detail|summary/i.test(key)
|
|
152
|
+
? '[redacted-sensitive-error]'
|
|
153
|
+
: '[redacted-sensitive]';
|
|
154
|
+
}
|
|
155
|
+
return value;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function findTransportPhysicalReportLeaks(value, path = '$', leaks = []) {
|
|
159
|
+
if (Array.isArray(value)) {
|
|
160
|
+
value.forEach((item, index) => findTransportPhysicalReportLeaks(item, `${path}[${index}]`, leaks));
|
|
161
|
+
return leaks;
|
|
162
|
+
}
|
|
163
|
+
if (value && typeof value === 'object') {
|
|
164
|
+
for (const [key, child] of Object.entries(value)) {
|
|
165
|
+
if (isSecretKey(key)
|
|
166
|
+
|| (isRawNetworkKey(key) && child !== '[redacted-network]')) {
|
|
167
|
+
leaks.push(`${path}.${key}`);
|
|
168
|
+
}
|
|
169
|
+
if (isIdentifierKey(key)
|
|
170
|
+
&& child !== ''
|
|
171
|
+
&& child !== '[redacted-id]') {
|
|
172
|
+
leaks.push(`${path}.${key}`);
|
|
173
|
+
}
|
|
174
|
+
findTransportPhysicalReportLeaks(child, `${path}.${key}`, leaks);
|
|
175
|
+
}
|
|
176
|
+
return leaks;
|
|
177
|
+
}
|
|
178
|
+
if (typeof value === 'string'
|
|
179
|
+
&& (containsRawNetworkText(value)
|
|
180
|
+
|| SECRET_TEXT.test(value)
|
|
181
|
+
|| UUID_TEXT.test(value))) {
|
|
182
|
+
leaks.push(path);
|
|
183
|
+
}
|
|
184
|
+
return leaks;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function optionalFinite(value) {
|
|
188
|
+
if (value === null || value === undefined || value === '') return null;
|
|
189
|
+
const parsed = Number(value);
|
|
190
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function phaseFrame(snapshot, phase) {
|
|
194
|
+
return Array.isArray(snapshot?.frames)
|
|
195
|
+
? snapshot.frames.find(frame => frame?.phase === phase && frame?.accepted === true)
|
|
196
|
+
: null;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function expectedPathForPhase(phase) {
|
|
200
|
+
if (phase === 'p2p') return 'p2p';
|
|
201
|
+
if (phase === 'relay') return 'relay';
|
|
202
|
+
return 'direct';
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function hubObservedTransport(frame) {
|
|
206
|
+
const transport = String(frame?.transport || '').trim().toLowerCase();
|
|
207
|
+
if (transport === 'udp-p2p') return { path: 'p2p', label: 'udp-p2p' };
|
|
208
|
+
if (transport === 'relay-binary') return { path: 'relay', label: 'relay-binary' };
|
|
209
|
+
if (transport === 'binary' || transport === 'tcp-binary') {
|
|
210
|
+
return { path: 'direct', label: 'direct-tcp' };
|
|
211
|
+
}
|
|
212
|
+
if (transport === 'ws-binary') return { path: 'direct', label: 'direct-ws' };
|
|
213
|
+
return { path: '', label: 'unknown' };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function isTransportPhysicalPhaseEvidence(frame, phase) {
|
|
217
|
+
const expectedPath = expectedPathForPhase(phase);
|
|
218
|
+
const observed = hubObservedTransport(frame);
|
|
219
|
+
const phaseLatencyMs = optionalFinite(frame?.phaseLatencyMs);
|
|
220
|
+
return frame?.accepted === true
|
|
221
|
+
&& frame?.phase === phase
|
|
222
|
+
&& frame?.actualPath === expectedPath
|
|
223
|
+
&& observed.path === expectedPath
|
|
224
|
+
&& frame?.isKeyFrame === true
|
|
225
|
+
&& frame?.annexB === true
|
|
226
|
+
&& frame?.hasIdr === true
|
|
227
|
+
&& frame?.hasSps === true
|
|
228
|
+
&& frame?.hasPps === true
|
|
229
|
+
&& frame?.nalOrderValid === true
|
|
230
|
+
&& optionalFinite(frame?.byteLength) > 0
|
|
231
|
+
&& phaseLatencyMs !== null
|
|
232
|
+
&& phaseLatencyMs >= 0
|
|
233
|
+
&& phaseLatencyMs <= MAX_PHASE_LATENCY_MS;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function cumulativeDelta(values) {
|
|
237
|
+
if (!Array.isArray(values) || values.length < 2) {
|
|
238
|
+
return { valid: false, delta: null };
|
|
239
|
+
}
|
|
240
|
+
const normalized = values.map(optionalFinite);
|
|
241
|
+
const valid = normalized.every(value => value !== null && value >= 0)
|
|
242
|
+
&& normalized.every((value, index) => index === 0 || value >= normalized[index - 1]);
|
|
243
|
+
return {
|
|
244
|
+
valid,
|
|
245
|
+
delta: valid ? normalized.at(-1) - normalized[0] : null
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function scopedCounterDelta(baselines, samples) {
|
|
250
|
+
const scopes = new Map();
|
|
251
|
+
for (const baseline of baselines || []) {
|
|
252
|
+
const scope = String(baseline?.scope || '');
|
|
253
|
+
const value = optionalFinite(baseline?.value);
|
|
254
|
+
if (!scope || value === null || value < 0 || scopes.has(scope)) {
|
|
255
|
+
return { valid: false, delta: null };
|
|
256
|
+
}
|
|
257
|
+
scopes.set(scope, { initial: value, last: value });
|
|
258
|
+
}
|
|
259
|
+
let total = 0;
|
|
260
|
+
for (const sample of samples || []) {
|
|
261
|
+
const scope = String(sample?.scope || '');
|
|
262
|
+
const value = optionalFinite(sample?.value);
|
|
263
|
+
if (!scope || value === null || value < 0) {
|
|
264
|
+
return { valid: false, delta: null };
|
|
265
|
+
}
|
|
266
|
+
const state = scopes.get(scope) || { initial: 0, last: 0 };
|
|
267
|
+
if (value < state.last) {
|
|
268
|
+
return { valid: false, delta: null };
|
|
269
|
+
}
|
|
270
|
+
state.last = value;
|
|
271
|
+
scopes.set(scope, state);
|
|
272
|
+
}
|
|
273
|
+
for (const state of scopes.values()) {
|
|
274
|
+
total += state.last - state.initial;
|
|
275
|
+
}
|
|
276
|
+
return { valid: true, delta: total };
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function timestampWithin(value, reference, maximumSkewMs) {
|
|
280
|
+
const observedAt = Date.parse(String(value || ''));
|
|
281
|
+
const referenceAt = Date.parse(String(reference || ''));
|
|
282
|
+
return Number.isFinite(observedAt)
|
|
283
|
+
&& Number.isFinite(referenceAt)
|
|
284
|
+
&& Math.abs(observedAt - referenceAt) <= maximumSkewMs;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export function classifyTransportPhysicalSnapshot(snapshot) {
|
|
288
|
+
const checks = [];
|
|
289
|
+
const add = (id, ok, detail) => checks.push({ id, ok: ok === true, detail });
|
|
290
|
+
const owner = snapshot?.owner || {};
|
|
291
|
+
|
|
292
|
+
add(
|
|
293
|
+
'same-current-owner',
|
|
294
|
+
!!owner.streamId
|
|
295
|
+
&& !!owner.streamCommandId
|
|
296
|
+
&& Number(owner.captureGeneration || 0) > 0
|
|
297
|
+
&& Number(owner.monitorIndex) >= 0,
|
|
298
|
+
'One immutable stream, command, generation, and monitor must own the complete run.');
|
|
299
|
+
|
|
300
|
+
const observedFrames = REQUIRED_PHASES.map(phase => phaseFrame(snapshot, phase));
|
|
301
|
+
for (let index = 0; index < REQUIRED_PHASES.length; index += 1) {
|
|
302
|
+
const phase = REQUIRED_PHASES[index];
|
|
303
|
+
const frame = observedFrames[index];
|
|
304
|
+
const expectedPath = expectedPathForPhase(phase);
|
|
305
|
+
const observed = hubObservedTransport(frame);
|
|
306
|
+
add(
|
|
307
|
+
`received-h264-${phase}`,
|
|
308
|
+
!!frame
|
|
309
|
+
&& frame.actualPath === expectedPath
|
|
310
|
+
&& frame.isKeyFrame === true
|
|
311
|
+
&& frame.annexB === true
|
|
312
|
+
&& frame.hasIdr === true
|
|
313
|
+
&& frame.hasSps === true
|
|
314
|
+
&& frame.hasPps === true
|
|
315
|
+
&& frame.nalOrderValid === true
|
|
316
|
+
&& optionalFinite(frame.byteLength) > 0,
|
|
317
|
+
`A received Annex-B H.264 SPS -> PPS -> IDR access unit must prove ${phase}.`);
|
|
318
|
+
add(
|
|
319
|
+
`hub-observed-transport-${phase}`,
|
|
320
|
+
!!frame && observed.path === expectedPath,
|
|
321
|
+
`Hub ingress must observe ${phase} as ${observed.label}.`);
|
|
322
|
+
const phaseLatencyMs = optionalFinite(frame?.phaseLatencyMs);
|
|
323
|
+
add(
|
|
324
|
+
`phase-latency-${phase}`,
|
|
325
|
+
phaseLatencyMs !== null
|
|
326
|
+
&& phaseLatencyMs >= 0
|
|
327
|
+
&& phaseLatencyMs <= MAX_PHASE_LATENCY_MS,
|
|
328
|
+
`${phaseLatencyMs === null ? 'unknown' : Math.round(phaseLatencyMs)}ms/15000ms`);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const receivedTimes = observedFrames.map(frame => Date.parse(frame?.receivedAt || ''));
|
|
332
|
+
add(
|
|
333
|
+
'ordered-path-transition',
|
|
334
|
+
observedFrames.every(Boolean)
|
|
335
|
+
&& receivedTimes.every(Number.isFinite)
|
|
336
|
+
&& receivedTimes.every((time, index) => index === 0 || time > receivedTimes[index - 1]),
|
|
337
|
+
'Frames must arrive in strict Direct, UDP P2P, encrypted relay, Direct order.');
|
|
338
|
+
|
|
339
|
+
const frameSequences = observedFrames.map(frame => optionalFinite(frame?.frameSeq));
|
|
340
|
+
add(
|
|
341
|
+
'strict-frame-sequence',
|
|
342
|
+
frameSequences.every(value => Number.isSafeInteger(value) && value > 0)
|
|
343
|
+
&& frameSequences.every((value, index) => index === 0 || value > frameSequences[index - 1]),
|
|
344
|
+
frameSequences.map(value => value ?? '?').join(' -> '));
|
|
345
|
+
|
|
346
|
+
const switchCounts = observedFrames.map(frame => optionalFinite(frame?.switchCount));
|
|
347
|
+
add(
|
|
348
|
+
'transport-switch-count',
|
|
349
|
+
switchCounts.every(value => Number.isSafeInteger(value) && value >= 0)
|
|
350
|
+
&& switchCounts.every((value, index) => index === 0 || value > switchCounts[index - 1]),
|
|
351
|
+
switchCounts.map(value => value ?? '?').join(' -> '));
|
|
352
|
+
|
|
353
|
+
const network = snapshot?.network || {};
|
|
354
|
+
add(
|
|
355
|
+
'distinct-public-ipv4-egress',
|
|
356
|
+
network.evidence === 'authenticated-udp-endpoint-rendezvous-observed'
|
|
357
|
+
&& network.endpointAuthenticated === true
|
|
358
|
+
&& network.clientAddressPublic === true
|
|
359
|
+
&& network.hubAddressPublic === true
|
|
360
|
+
&& network.differentPublicEgress === true
|
|
361
|
+
&& network.hubEndpointSource === 'trusted-rendezvous-observation',
|
|
362
|
+
'Authenticated UDP and trusted-rendezvous-observed distinct public IPv4 egress values are required; this does not prove distinct NAT devices.');
|
|
363
|
+
add(
|
|
364
|
+
'fresh-hub-public-ipv4-observation',
|
|
365
|
+
timestampWithin(
|
|
366
|
+
network.hubAddressObservedAt,
|
|
367
|
+
snapshot?.startedAt,
|
|
368
|
+
MAX_HUB_ADDRESS_OBSERVATION_SKEW_MS),
|
|
369
|
+
'The Hub public IPv4 value must be freshly observed within 60 seconds of diagnostic start.');
|
|
370
|
+
add(
|
|
371
|
+
'fresh-client-udp-observation',
|
|
372
|
+
timestampWithin(
|
|
373
|
+
network.observedAt,
|
|
374
|
+
snapshot?.startedAt,
|
|
375
|
+
MAX_HUB_ADDRESS_OBSERVATION_SKEW_MS),
|
|
376
|
+
'The authenticated Client UDP endpoint must be observed within 60 seconds of diagnostic start.');
|
|
377
|
+
|
|
378
|
+
const cleanup = Array.isArray(snapshot?.cleanup) ? snapshot.cleanup : [];
|
|
379
|
+
add(
|
|
380
|
+
'lease-cleaned',
|
|
381
|
+
snapshot?.state === 'completed'
|
|
382
|
+
&& cleanup.some(entry => entry?.state === 'agent-released'),
|
|
383
|
+
'The exact-owner diagnostic lease must be released by the Agent.');
|
|
384
|
+
|
|
385
|
+
const baseline = snapshot?.baseline || {};
|
|
386
|
+
const agentTelemetryAvailable = baseline.agentQueueTelemetryAvailable === true
|
|
387
|
+
&& optionalFinite(baseline.agentQueueDropped) !== null
|
|
388
|
+
&& observedFrames.every(frame =>
|
|
389
|
+
frame?.agentQueueTelemetryAvailable === true
|
|
390
|
+
&& optionalFinite(frame.agentQueueDepth) !== null
|
|
391
|
+
&& optionalFinite(frame.agentQueueDropped) !== null);
|
|
392
|
+
const hubTelemetryAvailable = baseline.hubIngressTelemetryAvailable === true
|
|
393
|
+
&& optionalFinite(baseline.hubIngressDropped) !== null
|
|
394
|
+
&& !!baseline.hubIngressCounterEpochHash
|
|
395
|
+
&& !!baseline.udpCounterEpochHash
|
|
396
|
+
&& observedFrames.every(frame =>
|
|
397
|
+
frame?.hubIngressTelemetryAvailable === true
|
|
398
|
+
&& optionalFinite(frame.hubIngressDropped) !== null
|
|
399
|
+
&& !!frame.hubIngressCounterEpochHash);
|
|
400
|
+
const p2pFrame = observedFrames[REQUIRED_PHASES.indexOf('p2p')];
|
|
401
|
+
const udpTelemetryAvailable = baseline.udpTelemetryAvailable === true
|
|
402
|
+
&& optionalFinite(baseline.udpIncompleteFrames) !== null
|
|
403
|
+
&& p2pFrame?.udpTelemetryAvailable === true
|
|
404
|
+
&& optionalFinite(p2pFrame?.udpIncompleteFrames) !== null
|
|
405
|
+
&& p2pFrame?.hubIngressCounterEpochHash === baseline.udpCounterEpochHash;
|
|
406
|
+
add(
|
|
407
|
+
'agent-queue-telemetry',
|
|
408
|
+
agentTelemetryAvailable,
|
|
409
|
+
agentTelemetryAvailable ? 'baseline and all four phases' : 'unknown or incomplete');
|
|
410
|
+
add(
|
|
411
|
+
'hub-ingress-telemetry',
|
|
412
|
+
hubTelemetryAvailable,
|
|
413
|
+
hubTelemetryAvailable ? 'baseline and all four phases' : 'unknown or incomplete');
|
|
414
|
+
add(
|
|
415
|
+
'udp-reassembly-telemetry',
|
|
416
|
+
udpTelemetryAvailable,
|
|
417
|
+
udpTelemetryAvailable ? 'baseline and P2P phase' : 'unknown or incomplete');
|
|
418
|
+
|
|
419
|
+
const agentDropped = cumulativeDelta([
|
|
420
|
+
baseline.agentQueueDropped,
|
|
421
|
+
...observedFrames.map(frame => frame?.agentQueueDropped)
|
|
422
|
+
]);
|
|
423
|
+
const hubDropped = scopedCounterDelta([
|
|
424
|
+
{
|
|
425
|
+
scope: baseline.hubIngressCounterEpochHash,
|
|
426
|
+
value: baseline.hubIngressDropped
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
scope: baseline.udpCounterEpochHash,
|
|
430
|
+
value: baseline.udpIncompleteFrames
|
|
431
|
+
}
|
|
432
|
+
], observedFrames.map(frame => ({
|
|
433
|
+
scope: frame?.hubIngressCounterEpochHash,
|
|
434
|
+
value: frame?.hubIngressDropped
|
|
435
|
+
})));
|
|
436
|
+
const udpIncomplete = cumulativeDelta([
|
|
437
|
+
baseline.udpIncompleteFrames,
|
|
438
|
+
p2pFrame?.udpIncompleteFrames
|
|
439
|
+
]);
|
|
440
|
+
const maxAgentQueueDepth = agentTelemetryAvailable
|
|
441
|
+
? Math.max(...observedFrames.map(frame => Number(frame.agentQueueDepth)))
|
|
442
|
+
: null;
|
|
443
|
+
const boundedQueues = agentTelemetryAvailable
|
|
444
|
+
&& hubTelemetryAvailable
|
|
445
|
+
&& udpTelemetryAvailable
|
|
446
|
+
&& agentDropped.valid
|
|
447
|
+
&& hubDropped.valid
|
|
448
|
+
&& udpIncomplete.valid
|
|
449
|
+
&& maxAgentQueueDepth <= MAX_AGENT_QUEUE_DEPTH
|
|
450
|
+
&& agentDropped.delta <= MAX_AGENT_QUEUE_DROPPED_DELTA
|
|
451
|
+
&& hubDropped.delta <= MAX_HUB_INGRESS_DROPPED_DELTA
|
|
452
|
+
&& udpIncomplete.delta <= MAX_UDP_INCOMPLETE_DELTA;
|
|
453
|
+
add(
|
|
454
|
+
'bounded-frame-queues',
|
|
455
|
+
boundedQueues,
|
|
456
|
+
`queueDepth=${maxAgentQueueDepth ?? 'unknown'}, `
|
|
457
|
+
+ `agentDroppedDelta=${agentDropped.delta ?? 'unknown'}, `
|
|
458
|
+
+ `hubDroppedDelta=${hubDropped.delta ?? 'unknown'}, `
|
|
459
|
+
+ `udpIncompleteDelta=${udpIncomplete.delta ?? 'unknown'}`);
|
|
460
|
+
|
|
461
|
+
const failed = checks.filter(check => !check.ok);
|
|
462
|
+
const phaseLatencies = observedFrames.map(frame => optionalFinite(frame?.phaseLatencyMs));
|
|
463
|
+
return {
|
|
464
|
+
status: failed.length === 0 ? 'passed' : 'blocked',
|
|
465
|
+
summary: failed.length === 0
|
|
466
|
+
? 'Direct, UDP P2P, encrypted relay, and Direct recovery were proven by independent received H.264 access units.'
|
|
467
|
+
: `Physical transport proof is incomplete (${failed.map(check => check.id).join(', ')}).`,
|
|
468
|
+
checks,
|
|
469
|
+
metrics: {
|
|
470
|
+
maxPhaseLatencyMs: phaseLatencies.every(value => value !== null)
|
|
471
|
+
? Math.max(...phaseLatencies)
|
|
472
|
+
: null,
|
|
473
|
+
maxAgentQueueDepth,
|
|
474
|
+
agentQueueDroppedDelta: agentDropped.delta,
|
|
475
|
+
hubIngressDroppedDelta: hubDropped.delta,
|
|
476
|
+
udpIncompleteFramesDelta: udpIncomplete.delta,
|
|
477
|
+
observedTransports: observedFrames.map(frame => hubObservedTransport(frame).label)
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
export function buildTransportPhysicalReport(snapshot, context = {}) {
|
|
483
|
+
const classified = classifyTransportPhysicalSnapshot(snapshot);
|
|
484
|
+
const report = redactTransportPhysicalReport({
|
|
485
|
+
schema: 'livedesk.transport-physical.v1',
|
|
486
|
+
generatedAt: new Date().toISOString(),
|
|
487
|
+
hubUrl: context.hubUrl,
|
|
488
|
+
selectedDeviceId: context.deviceId,
|
|
489
|
+
result: classified,
|
|
490
|
+
transport: snapshot
|
|
491
|
+
});
|
|
492
|
+
const leaks = findTransportPhysicalReportLeaks(report);
|
|
493
|
+
if (leaks.length > 0) {
|
|
494
|
+
throw new Error(`transport-physical-report-redaction-failed:${leaks.join(',')}`);
|
|
495
|
+
}
|
|
496
|
+
return report;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
export {
|
|
500
|
+
MAX_HUB_ADDRESS_OBSERVATION_SKEW_MS,
|
|
501
|
+
MAX_PHASE_LATENCY_MS,
|
|
502
|
+
REQUIRED_PHASES
|
|
503
|
+
};
|