Add native self-hosted instance connection to fluxer_desktop

Trimmed monorepo checkout (fluxer_desktop + packages/voice_engine_v2 +
tools/ci) with a "Connect to a Different Server" menu item and popout
that lets the desktop app switch to any self-hosted Fluxer instance,
plus fixes for well-known discovery on single-domain self-hosted
deployments and a false-positive ERR_ABORTED on same-origin client
redirects during the switch. Defaults to chat.fluxr.chat and uses an
isolated userData directory from the official build.
This commit is contained in:
2026-07-01 18:22:43 -04:00
commit 682afacd30
1763 changed files with 613720 additions and 0 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,842 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import assert from 'node:assert/strict';
import {describe, test} from 'node:test';
import {
buildReport,
buildScenarioConfigs,
createLiveKitAdminToken,
expectedCodecMime,
jwtSubject,
liveKitApiUrl,
liveKitTcpTarget,
parseCodecList,
parseConfig,
sanitizeConfig,
scenarioRoomName,
serverPublishedTrackChecks,
statsHasOutbound,
statsHasOutboundCodec,
strictFailures,
} from './livekit-harness.mjs';
function tokenForSubject(subject) {
const payload = Buffer.from(JSON.stringify({sub: subject}), 'utf8').toString('base64url');
return `header.${payload}.signature`;
}
function jwtPayload(token) {
const parts = token.split('.');
return JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8'));
}
async function withHarnessEnv(env, callback) {
const previous = {...process.env};
for (const key of Object.keys(process.env)) {
if (
key.startsWith('LIVEKIT_') ||
key.startsWith('FLUXER_WEBRTC_SENDER_LIVEKIT') ||
key.startsWith('FLUXER_NATIVE_MEDIA')
) {
delete process.env[key];
}
}
Object.assign(process.env, env);
try {
return await callback();
} finally {
for (const key of Object.keys(process.env)) {
delete process.env[key];
}
Object.assign(process.env, previous);
}
}
function strictReportFor(
config,
{
videoFrameTimes = null,
videoFrameRecords = null,
audioFrameTimes = [],
droppedVideoFrameCallbacks = 0,
subscriberStatsSamples = [],
} = {},
) {
const strictStartedAtMs = 1_000;
const strictEndedAtMs = 2_000;
const effectiveVideoFrameRecords =
videoFrameRecords ??
(videoFrameTimes ?? []).map((atMs) => ({
atMs,
identity: config.publisherIdentity,
kind: 'video',
source: 'screen_share',
trackName: 'screen',
trackSid: 'TR_screen',
width: config.expectedWidth,
height: config.expectedHeight,
}));
const effectiveVideoFrameTimes = videoFrameTimes ?? effectiveVideoFrameRecords.map((record) => record.atMs);
return buildReport({
status: 'pass',
config,
context: {
startedAtMs: strictStartedAtMs - 500,
startedAtIso: new Date(strictStartedAtMs - 500).toISOString(),
strictStartedAtMs,
strictEndedAtMs,
publisherState: {statsSamples: [], stats: null},
subscriberState: {
statsSamples: subscriberStatsSamples,
stats: subscriberStatsSamples.at(-1)?.payload ?? null,
videoCallbacks: effectiveVideoFrameTimes.length,
videoBytes: effectiveVideoFrameTimes.length * 4,
videoFrameTimes: effectiveVideoFrameTimes,
videoFrameRecords: effectiveVideoFrameRecords,
audioFrameTimes,
lastVideoMeta: effectiveVideoFrameRecords.at(-1) ?? null,
lastVideoFrameAtMs: effectiveVideoFrameTimes.at(-1) ?? null,
lastAudioFrameAtMs: audioFrameTimes.at(-1) ?? null,
},
subscriber: {
inboundAudioFrames: () => audioFrameTimes.length,
inboundVideoFrames: () => effectiveVideoFrameTimes.length,
droppedVideoFrameCallbacks: () => droppedVideoFrameCallbacks,
},
},
});
}
function findCheck(checks, name) {
const check = checks.find((entry) => entry.name === name);
assert.ok(check, `missing check: ${name}`);
return check;
}
function serverChecksFor(config, serverState) {
return serverPublishedTrackChecks({
config,
serverState,
publisherIdentity: config.publisherIdentity,
secondaryPublisherIdentity: config.secondaryPublisherIdentity,
});
}
describe('livekit harness setup matrix', () => {
test('normalizes LiveKit screenshare source spelling in outbound stats', () => {
const stats = {
outbound: [
{
trackSid: 'TR_screen',
source: 'screenshare',
kind: 'video',
codec: 'video/VP8',
bitrateKbps: 400,
packetsLost: 0,
},
],
};
assert.equal(statsHasOutbound(stats, 'video', 'screen_share'), true);
assert.equal(statsHasOutboundCodec(stats, 'video', 'screen_share', 'video/VP8'), true);
assert.equal(statsHasOutboundCodec(stats, 'video', 'screen_share', 'video/H264'), false);
});
test('maps all supported codec spellings to expected mime types', () => {
assert.equal(expectedCodecMime('vp8'), 'video/VP8');
assert.equal(expectedCodecMime('VP9'), 'video/VP9');
assert.equal(expectedCodecMime('h264'), 'video/H264');
assert.equal(expectedCodecMime('av1'), 'video/AV1');
assert.equal(expectedCodecMime('h265'), 'video/H265');
assert.equal(expectedCodecMime('hevc'), 'video/H265');
assert.equal(expectedCodecMime(''), null);
assert.throws(() => expectedCodecMime('h266'), /unsupported/);
});
test('parses codec lists with fallback and rejects empty effective lists', () => {
assert.deepEqual(parseCodecList(' vp8, h264,, hevc ', 'av1'), ['vp8', 'h264', 'hevc']);
assert.deepEqual(parseCodecList('', 'vp9'), ['vp9']);
assert.throws(() => parseCodecList('', ''), /at least one/);
assert.throws(() => parseCodecList('vp8,h266', 'vp8'), /unsupported/);
});
test('default non-strict config keeps the fast local live setup small', async () => {
await withHarnessEnv({}, () => {
const config = parseConfig();
assert.equal(config.url, 'ws://localhost:7880');
assert.equal(config.serverApiUrl, 'http://localhost:7880');
assert.deepEqual(config.screenCodecs, ['vp8']);
assert.equal(config.expectedScreenCodec, 'video/VP8');
assert.equal(config.secondaryPublisher, false);
assert.equal(config.microphone, true);
assert.equal(config.screenAudio, false);
assert.equal(config.dataPacket, false);
assert.equal(config.subscriptionCycle, false);
assert.equal(config.validateServerPublishing, false);
assert.equal(config.durationMs, 0);
assert.equal(config.externalTokens, false);
assert.equal(config.screenSimulcast, true);
assert.equal(config.secondaryPublisherScreenSimulcast, true);
assert.equal(config.adaptiveSend, true);
assert.equal(config.minVideoFps, 15);
assert.equal(config.minResolutionScale, 0.5);
assert.equal(config.maxAudioBufferMs, 750);
});
});
test('strict config expands to multi-codec secondary-publisher scenarios', async () => {
await withHarnessEnv(
{
LIVEKIT_HARNESS_STRICT: '1',
LIVEKIT_ROOM: 'matrix-room',
LIVEKIT_SCREEN_CODECS: 'vp8,h264,hevc',
LIVEKIT_EXPECT_SCREEN_CODECS: 'vp8,h264,h265',
},
() => {
const config = parseConfig();
assert.equal(config.secondaryPublisher, true);
assert.equal(config.screenAudio, true);
assert.equal(config.dataPacket, true);
assert.equal(config.subscriptionCycle, true);
assert.equal(config.validateServerPublishing, true);
assert.equal(config.durationMs, 10 * 60 * 1000);
assert.equal(config.maxAudioFrameGapMs, 250);
assert.equal(config.maxPacketLoss, null);
assert.equal(config.requireStableResolution, true);
assert.equal(config.videoPattern, 'gradient');
const scenarios = buildScenarioConfigs(config);
assert.deepEqual(
scenarios.map((scenario) => scenario.scenarioName),
['codec-vp8-with-h264', 'codec-h264-with-hevc', 'codec-hevc-with-vp8'],
);
assert.deepEqual(
scenarios.map((scenario) => scenario.expectedScreenCodec),
['video/VP8', 'video/H264', 'video/H265'],
);
assert.deepEqual(
scenarios.map((scenario) => scenario.expectedSecondaryPublisherCodec),
['video/H264', 'video/H265', 'video/VP8'],
);
assert.equal(scenarios[0].room, 'matrix-room-codec-vp8-with-h264');
assert.equal(scenarios[1].reportPath, null);
},
);
});
test('stress knobs parse packet loss, resolution stability, audio gap, and video pattern', async () => {
await withHarnessEnv(
{
LIVEKIT_MAX_PACKET_LOSS: '0',
LIVEKIT_REQUIRE_STABLE_RESOLUTION: '0',
LIVEKIT_MAX_AUDIO_FRAME_GAP_MS: '180',
LIVEKIT_VIDEO_PATTERN: 'fast',
LIVEKIT_VIDEO_INPUT: 'nv12',
LIVEKIT_SUBSCRIBER_VIDEO_QUALITY: 'high',
LIVEKIT_SCREEN_SIMULCAST: '0',
LIVEKIT_SECOND_PUBLISHER_SCREEN_SIMULCAST: '1',
LIVEKIT_SCREEN_FPS: '60',
LIVEKIT_ADAPTIVE_SEND: '0',
LIVEKIT_MIN_VIDEO_FPS: '24',
LIVEKIT_MIN_RESOLUTION_SCALE: '0.75',
LIVEKIT_MAX_AUDIO_BUFFER_MS: '640',
},
() => {
const config = parseConfig();
assert.equal(config.maxPacketLoss, 0);
assert.equal(config.requireStableResolution, false);
assert.equal(config.maxAudioFrameGapMs, 180);
assert.equal(config.videoPattern, 'fast');
assert.equal(config.videoInput, 'nv12');
assert.equal(config.subscriberVideoQuality, 'high');
assert.equal(config.screenSimulcast, false);
assert.equal(config.secondaryPublisherScreenSimulcast, true);
assert.equal(config.adaptiveSend, false);
assert.equal(config.minVideoFps, 24);
assert.equal(config.minResolutionScale, 0.75);
assert.equal(config.maxAudioBufferMs, 640);
},
);
});
test('stress knobs reject impossible send pacing configurations', async () => {
await withHarnessEnv({LIVEKIT_SCREEN_FPS: '0'}, () =>
assert.throws(() => parseConfig(), /LIVEKIT_SCREEN_FPS must be a positive number/),
);
await withHarnessEnv({LIVEKIT_MIN_VIDEO_FPS: '0'}, () =>
assert.throws(() => parseConfig(), /LIVEKIT_MIN_VIDEO_FPS must be a positive number/),
);
await withHarnessEnv({LIVEKIT_SCREEN_FPS: '30', LIVEKIT_MIN_VIDEO_FPS: '60'}, () =>
assert.throws(() => parseConfig(), /MIN_VIDEO_FPS must be less than or equal/),
);
await withHarnessEnv({LIVEKIT_MAX_AUDIO_BUFFER_MS: '0'}, () =>
assert.throws(() => parseConfig(), /LIVEKIT_MAX_AUDIO_BUFFER_MS must be a positive integer/),
);
await withHarnessEnv({LIVEKIT_MIN_RESOLUTION_SCALE: '0'}, () =>
assert.throws(() => parseConfig(), /LIVEKIT_MIN_RESOLUTION_SCALE must be a positive number/),
);
await withHarnessEnv({LIVEKIT_MIN_RESOLUTION_SCALE: '1.5'}, () =>
assert.throws(() => parseConfig(), /LIVEKIT_MIN_RESOLUTION_SCALE must be greater than 0/),
);
await withHarnessEnv({LIVEKIT_ENABLE_SCREEN_AUDIO: 'maybe'}, () =>
assert.throws(() => parseConfig(), /LIVEKIT_ENABLE_SCREEN_AUDIO must be a boolean flag/),
);
await withHarnessEnv({LIVEKIT_SCREEN_CODECS: 'vp8,h264', LIVEKIT_EXPECT_SCREEN_CODECS: 'vp8'}, () =>
assert.throws(() => parseConfig(), /EXPECT_SCREEN_CODECS length must match/),
);
});
test('explicit secondary codec expectations override rotating defaults', async () => {
await withHarnessEnv(
{
LIVEKIT_ENABLE_SECOND_PUBLISHER: '1',
LIVEKIT_SCREEN_CODECS: 'vp8,h264',
LIVEKIT_SECOND_PUBLISHER_CODEC: 'av1',
LIVEKIT_EXPECT_SECOND_PUBLISHER_SCREEN_CODEC: 'hevc',
},
() => {
const scenarios = buildScenarioConfigs(parseConfig());
assert.deepEqual(
scenarios.map((scenario) => scenario.secondaryPublisherCodec),
['av1', 'av1'],
);
assert.deepEqual(
scenarios.map((scenario) => scenario.expectedSecondaryPublisherCodec),
['video/H265', 'video/H265'],
);
},
);
});
test('secondary screen codec aliases are accepted and conflicting aliases are rejected', async () => {
await withHarnessEnv(
{
LIVEKIT_ENABLE_SECOND_PUBLISHER: '1',
LIVEKIT_SCREEN_CODECS: 'h264',
LIVEKIT_SECOND_PUBLISHER_SCREEN_CODECS: 'vp8',
LIVEKIT_EXPECT_SECONDARY_PUBLISHER_SCREEN_CODEC: 'vp8',
},
() => {
const config = parseConfig();
assert.equal(config.secondaryPublisherCodec, 'vp8');
assert.equal(config.expectedSecondaryPublisherCodec, 'video/VP8');
},
);
await withHarnessEnv(
{
LIVEKIT_ENABLE_SECOND_PUBLISHER: '1',
LIVEKIT_SECOND_PUBLISHER_CODEC: 'h264',
LIVEKIT_SECOND_PUBLISHER_SCREEN_CODEC: 'vp8',
},
() => assert.throws(() => parseConfig(), /conflicts with LIVEKIT_SECOND_PUBLISHER_CODEC/),
);
await withHarnessEnv(
{
LIVEKIT_ENABLE_SECOND_PUBLISHER: '1',
LIVEKIT_EXPECT_SECOND_PUBLISHER_SCREEN_CODEC: 'h264',
LIVEKIT_EXPECT_SECONDARY_PUBLISHER_SCREEN_CODEC: 'vp8',
},
() => assert.throws(() => parseConfig(), /conflicts with LIVEKIT_EXPECT_SECOND_PUBLISHER_SCREEN_CODEC/),
);
});
test('external token setup derives identities, requires matching tokens, and redacts reports', async () => {
await withHarnessEnv(
{
LIVEKIT_PUBLISHER_TOKEN: tokenForSubject('publisher-subject'),
},
() => assert.throws(() => parseConfig(), /both required/),
);
await withHarnessEnv(
{
LIVEKIT_ROOM: 'shared-token-room',
LIVEKIT_SCREEN_CODECS: 'vp8,h264',
LIVEKIT_ENABLE_SECOND_PUBLISHER: '1',
LIVEKIT_PUBLISHER_TOKEN: tokenForSubject('publisher-subject'),
LIVEKIT_SUBSCRIBER_TOKEN: tokenForSubject('subscriber-subject'),
},
() => assert.throws(() => parseConfig(), /SECONDARY_PUBLISHER_TOKEN/),
);
await withHarnessEnv(
{
LIVEKIT_ROOM: 'shared-token-room',
LIVEKIT_SCREEN_CODECS: 'vp8,h264',
LIVEKIT_ENABLE_SECOND_PUBLISHER: '1',
LIVEKIT_PUBLISHER_TOKEN: tokenForSubject('publisher-subject'),
LIVEKIT_SUBSCRIBER_TOKEN: tokenForSubject('subscriber-subject'),
LIVEKIT_SECONDARY_PUBLISHER_TOKEN: tokenForSubject('secondary-subject'),
LIVEKIT_E2EE_KEY: 'secret-key',
},
() => {
const config = parseConfig();
assert.equal(config.externalTokens, true);
assert.equal(config.publisherIdentity, 'publisher-subject');
assert.equal(config.subscriberIdentity, 'subscriber-subject');
assert.equal(config.secondaryPublisherIdentity, 'secondary-subject');
assert.deepEqual(
buildScenarioConfigs(config).map((scenario) => scenario.room),
['shared-token-room', 'shared-token-room'],
);
const sanitized = sanitizeConfig(config);
assert.equal(sanitized.url, 'ws://localhost:7880');
assert.equal(sanitized.apiSecret, '<redacted>');
assert.equal(sanitized.e2eeKey, '<present>');
assert.equal(sanitized.publisherToken, '<present>');
assert.equal(sanitized.subscriberToken, '<present>');
assert.equal(sanitized.secondaryPublisherToken, '<present>');
},
);
});
test('report sanitization strips URL credentials, query strings, and fragments', async () => {
await withHarnessEnv(
{
LIVEKIT_URL: 'wss://user:password@example.test:443/rtc?access_token=secret#fragment',
},
() => {
const sanitized = sanitizeConfig(parseConfig());
assert.equal(sanitized.url, 'wss://example.test/rtc');
assert.doesNotMatch(JSON.stringify(sanitized), /user|password|access_token|secret|fragment/);
},
);
});
test('jwt subject and TCP target parsing catch malformed live setups early', () => {
assert.equal(jwtSubject(tokenForSubject('user_1_connection')), 'user_1_connection');
assert.throws(() => jwtSubject('not-a-jwt'), /must be a JWT/);
assert.throws(() => jwtSubject('header.bad-json.signature'), /failed to decode/);
assert.throws(() => jwtSubject(tokenForSubject('')), /does not contain/);
assert.deepEqual(liveKitTcpTarget('ws://localhost:7880'), {host: 'localhost', port: 7880});
assert.deepEqual(liveKitTcpTarget('wss://[::1]/rtc'), {host: '::1', port: 443});
assert.equal(liveKitApiUrl('ws://localhost:7880/rtc?token=secret'), 'http://localhost:7880');
assert.equal(liveKitApiUrl('wss://livekit.example.test/rtc'), 'https://livekit.example.test');
assert.throws(() => liveKitTcpTarget('https://localhost'), /must use ws/);
});
test('admin token has the room-scoped grant needed for server publishing validation', () => {
const token = createLiveKitAdminToken({apiKey: 'devkey', apiSecret: 'secret', room: 'room-a'});
const payload = jwtPayload(token);
assert.equal(payload.iss, 'devkey');
assert.equal(payload.video.room, 'room-a');
assert.equal(payload.video.roomAdmin, true);
assert.equal(payload.video.roomJoin, undefined);
});
test('scenario room names are stable and bounded for generated-token suites', () => {
assert.equal(scenarioRoomName('room', 'codec-vp8', 1, false), 'room');
assert.equal(scenarioRoomName('room', 'codec:vP8 with H264', 2, false), 'room-codec-vP8-with-H264');
assert.equal(scenarioRoomName('room', 'codec-vp8', 2, true), 'room');
const longRoom = scenarioRoomName('r'.repeat(120), 'codec-h264-with-hevc', 2, false);
assert.equal(longRoom.length, 128);
assert.ok(longRoom.startsWith('r'.repeat(120)));
});
test('strict report checks fail when the strict window has too few samples to measure gaps', async () => {
await withHarnessEnv(
{
LIVEKIT_MIN_RECEIVED_FPS_RATIO: '0',
LIVEKIT_MAX_FRAME_GAP_MS: '250',
LIVEKIT_MAX_AUDIO_FRAME_GAP_MS: '250',
LIVEKIT_MAX_AV_DRIFT_MS: '0',
},
() => {
const config = parseConfig();
const report = strictReportFor(config, {
videoFrameTimes: [1_250],
audioFrameTimes: [1_300],
});
assert.equal(report.metrics.strictVideoFrames, 1);
assert.equal(report.metrics.strictAudioFrames, 1);
assert.match(strictFailures(report, config).join('\n'), /video frame gap unavailable/);
assert.match(strictFailures(report, config).join('\n'), /audio frame gap unavailable/);
},
);
});
test('strict report checks use only samples inside the measured window', async () => {
await withHarnessEnv(
{
LIVEKIT_MIN_RECEIVED_FPS_RATIO: '0',
LIVEKIT_MAX_FRAME_GAP_MS: '250',
LIVEKIT_MAX_AUDIO_FRAME_GAP_MS: '250',
LIVEKIT_MAX_AV_DRIFT_MS: '0',
},
() => {
const config = parseConfig();
const report = strictReportFor(config, {
videoFrameTimes: [500, 1_050, 1_200, 2_500],
audioFrameTimes: [600, 1_100, 1_240, 2_600],
});
assert.equal(report.metrics.strictVideoFrames, 2);
assert.equal(report.metrics.strictAudioFrames, 2);
assert.equal(report.metrics.maxVideoFrameGapMs, 150);
assert.equal(report.metrics.maxAudioFrameGapMs, 140);
assert.deepEqual(strictFailures(report, config), []);
},
);
});
test('strict screen report ignores camera video callbacks for resolution and drift', async () => {
await withHarnessEnv(
{
LIVEKIT_ENABLE_CAMERA: '1',
LIVEKIT_MIN_RECEIVED_FPS_RATIO: '0',
LIVEKIT_MAX_FRAME_GAP_MS: '250',
LIVEKIT_MAX_AUDIO_FRAME_GAP_MS: '250',
LIVEKIT_MAX_AV_DRIFT_MS: '80',
},
() => {
const config = parseConfig();
const report = strictReportFor(config, {
videoFrameRecords: [
{
atMs: 1_010,
identity: config.publisherIdentity,
kind: 'video',
source: 'screen_share',
trackName: 'screen',
trackSid: 'TR_screen',
width: 320,
height: 180,
},
{
atMs: 1_025,
identity: config.publisherIdentity,
kind: 'video',
source: 'camera',
trackName: 'camera',
trackSid: 'TR_camera',
width: 480,
height: 360,
},
{
atMs: 1_080,
identity: config.publisherIdentity,
kind: 'video',
source: 'screen_share',
trackName: 'screen',
trackSid: 'TR_screen',
width: 320,
height: 180,
},
{
atMs: 1_220,
identity: config.publisherIdentity,
kind: 'video',
source: 'camera',
trackName: 'camera',
trackSid: 'TR_camera',
width: 480,
height: 360,
},
],
audioFrameTimes: [1_040, 1_100],
});
assert.equal(report.metrics.videoCallbacks, 4);
assert.equal(report.metrics.strictVideoFrames, 2);
assert.deepEqual(report.metrics.videoResolutionCounts, {'320x180': 2});
assert.equal(report.metrics.videoResolutionMismatchCount, 0);
assert.equal(report.metrics.avDriftMs, 20);
assert.deepEqual(strictFailures(report, config), []);
},
);
});
test('strict packet loss gate uses loss deltas inside the strict window', async () => {
await withHarnessEnv(
{
LIVEKIT_MIN_RECEIVED_FPS_RATIO: '0',
LIVEKIT_MAX_FRAME_GAP_MS: '0',
LIVEKIT_MAX_AUDIO_FRAME_GAP_MS: '0',
LIVEKIT_MAX_AV_DRIFT_MS: '0',
LIVEKIT_MAX_PACKET_LOSS: '0',
},
() => {
const config = parseConfig();
const report = strictReportFor(config, {
videoFrameTimes: [1_100, 1_200],
subscriberStatsSamples: [
{atMs: 900, payload: {outbound: [], inbound: [{kind: 'video', packetsLost: 4}]}},
{atMs: 1_500, payload: {outbound: [], inbound: [{kind: 'video', packetsLost: 4}]}},
],
});
assert.equal(report.metrics.maxObservedPacketLoss, 4);
assert.equal(report.metrics.maxObservedPacketLossDelta, 0);
assert.deepEqual(strictFailures(report, config), []);
const failingReport = strictReportFor(config, {
videoFrameTimes: [1_100, 1_200],
subscriberStatsSamples: [
{atMs: 900, payload: {outbound: [], inbound: [{kind: 'video', packetsLost: 4}]}},
{atMs: 1_500, payload: {outbound: [], inbound: [{kind: 'video', packetsLost: 5}]}},
],
});
assert.equal(failingReport.metrics.maxObservedPacketLossDelta, 1);
assert.match(strictFailures(failingReport, config).join('\n'), /packet loss delta 1/);
},
);
});
test('server publishing checks cover primary and secondary screen, audio, and camera tracks', async () => {
await withHarnessEnv(
{
LIVEKIT_VALIDATE_SERVER_PUBLISHING: '1',
LIVEKIT_ENABLE_SECOND_PUBLISHER: '1',
LIVEKIT_ENABLE_SCREEN_AUDIO: '1',
LIVEKIT_ENABLE_CAMERA: '1',
},
() => {
const config = parseConfig();
const serverState = {
participants: [
{
identity: config.publisherIdentity,
tracks: [
{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false, sid: 'TR_V1'},
{type: 'AUDIO', source: 'MICROPHONE', mimeType: 'audio/red', muted: false, sid: 'TR_A1'},
{type: 'AUDIO', source: 'SCREEN_SHARE_AUDIO', mimeType: 'audio/red', muted: false, sid: 'TR_A2'},
{type: 'VIDEO', source: 'CAMERA', muted: false, sid: 'TR_V2'},
],
},
{
identity: config.secondaryPublisherIdentity,
tracks: [
{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false, sid: 'TR_V3'},
{type: 'AUDIO', source: 'MICROPHONE', mimeType: 'audio/red', muted: false, sid: 'TR_A3'},
{type: 'AUDIO', source: 'SCREEN_SHARE_AUDIO', mimeType: 'audio/red', muted: false, sid: 'TR_A4'},
],
},
],
error: null,
};
const checks = serverChecksFor(config, serverState);
assert.equal(checks.length, 8);
assert.deepEqual(
checks.map((check) => [check.name, check.pass]),
[
['server publishing API participants listed', true],
['server sees publisher screenshare publication', true],
['server sees publisher microphone publication', true],
['server sees publisher screen-share audio publication', true],
['server sees publisher camera publication', true],
['server sees secondary publisher screenshare publication', true],
['server sees secondary publisher microphone publication', true],
['server sees secondary publisher screen-share audio publication', true],
],
);
},
);
});
test('server publishing checks fail closed on missing server tracks', async () => {
await withHarnessEnv({LIVEKIT_VALIDATE_SERVER_PUBLISHING: '1', LIVEKIT_ENABLE_SCREEN_AUDIO: '1'}, () => {
const config = parseConfig();
const checks = serverChecksFor(config, {
participants: [
{
identity: config.publisherIdentity,
tracks: [{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false}],
},
],
error: null,
});
assert.equal(findCheck(checks, 'server sees publisher screenshare publication').pass, true);
assert.equal(findCheck(checks, 'server sees publisher screen-share audio publication').pass, false);
});
});
test('server publishing checks reject wrong participant identity', async () => {
await withHarnessEnv({LIVEKIT_VALIDATE_SERVER_PUBLISHING: '1'}, () => {
const config = parseConfig();
const checks = serverChecksFor(config, {
participants: [
{
identity: config.subscriberIdentity,
tracks: [{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false}],
},
],
error: null,
});
assert.equal(findCheck(checks, 'server publishing API participants listed').pass, true);
assert.equal(findCheck(checks, 'server sees publisher screenshare publication').pass, false);
});
});
test('server publishing checks reject wrong kind, source, and screen mime', async () => {
await withHarnessEnv({LIVEKIT_VALIDATE_SERVER_PUBLISHING: '1', LIVEKIT_EXPECT_SCREEN_CODEC: 'h264'}, () => {
const config = parseConfig();
const wrongKindChecks = serverChecksFor(config, {
participants: [
{
identity: config.publisherIdentity,
tracks: [{type: 'AUDIO', source: 'SCREEN_SHARE', mimeType: 'video/H264', muted: false}],
},
],
error: null,
});
assert.equal(findCheck(wrongKindChecks, 'server sees publisher screenshare publication').pass, false);
const wrongSourceChecks = serverChecksFor(config, {
participants: [
{
identity: config.publisherIdentity,
tracks: [{type: 'VIDEO', source: 'CAMERA', mimeType: 'video/H264', muted: false}],
},
],
error: null,
});
assert.equal(findCheck(wrongSourceChecks, 'server sees publisher screenshare publication').pass, false);
const wrongMimeChecks = serverChecksFor(config, {
participants: [
{
identity: config.publisherIdentity,
tracks: [{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false}],
},
],
error: null,
});
assert.equal(findCheck(wrongMimeChecks, 'server sees publisher screenshare publication').pass, false);
const matchingChecks = serverChecksFor(config, {
participants: [
{
identity: config.publisherIdentity,
tracks: [{type: 'VIDEO', source: 'SCREEN_SHARE', mime_type: 'video/h264', muted: false}],
},
],
error: null,
});
assert.equal(findCheck(matchingChecks, 'server sees publisher screenshare publication').pass, true);
});
});
test('server publishing checks reject muted or missing mute state on expected tracks', async () => {
await withHarnessEnv({LIVEKIT_VALIDATE_SERVER_PUBLISHING: '1'}, () => {
const config = parseConfig();
const mutedChecks = serverChecksFor(config, {
participants: [
{
identity: config.publisherIdentity,
tracks: [{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: true}],
},
],
error: null,
});
assert.equal(findCheck(mutedChecks, 'server sees publisher screenshare publication').pass, false);
const missingMutedChecks = serverChecksFor(config, {
participants: [
{
identity: config.publisherIdentity,
tracks: [{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8'}],
},
],
error: null,
});
assert.equal(findCheck(missingMutedChecks, 'server sees publisher screenshare publication').pass, false);
});
});
test('server publishing checks handle protobuf numeric enums without accepting data tracks as video', async () => {
await withHarnessEnv({LIVEKIT_VALIDATE_SERVER_PUBLISHING: '1'}, () => {
const config = parseConfig();
const dataTrackChecks = serverChecksFor(config, {
participants: [
{
identity: config.publisherIdentity,
tracks: [{type: 2, source: 3, mimeType: 'video/VP8', muted: false}],
},
],
error: null,
});
assert.equal(findCheck(dataTrackChecks, 'server sees publisher screenshare publication').pass, false);
const videoTrackChecks = serverChecksFor(config, {
participants: [
{
identity: config.publisherIdentity,
tracks: [{type: 1, source: 3, mimeType: 'video/VP8', muted: false}],
},
],
error: null,
});
assert.equal(findCheck(videoTrackChecks, 'server sees publisher screenshare publication').pass, true);
});
});
test('server publishing checks fail closed on missing secondary, audio, camera, and screen-audio tracks', async () => {
await withHarnessEnv(
{
LIVEKIT_VALIDATE_SERVER_PUBLISHING: '1',
LIVEKIT_ENABLE_SECOND_PUBLISHER: '1',
LIVEKIT_ENABLE_SCREEN_AUDIO: '1',
LIVEKIT_ENABLE_CAMERA: '1',
},
() => {
const config = parseConfig();
const checks = serverChecksFor(config, {
participants: [
{
identity: config.publisherIdentity,
tracks: [{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false}],
},
{
identity: config.secondaryPublisherIdentity,
tracks: [{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false}],
},
],
error: null,
});
assert.equal(findCheck(checks, 'server sees publisher screenshare publication').pass, true);
assert.equal(findCheck(checks, 'server sees publisher microphone publication').pass, false);
assert.equal(findCheck(checks, 'server sees publisher screen-share audio publication').pass, false);
assert.equal(findCheck(checks, 'server sees publisher camera publication').pass, false);
assert.equal(findCheck(checks, 'server sees secondary publisher screenshare publication').pass, true);
assert.equal(findCheck(checks, 'server sees secondary publisher microphone publication').pass, false);
assert.equal(findCheck(checks, 'server sees secondary publisher screen-share audio publication').pass, false);
},
);
});
test('strict server publishing validation does not pass open on Twirp errors', async () => {
await withHarnessEnv({LIVEKIT_HARNESS_STRICT: '1'}, () => {
const config = parseConfig();
const checks = serverChecksFor(config, {
participants: [
{
identity: config.publisherIdentity,
tracks: [
{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false},
{type: 'AUDIO', source: 'MICROPHONE', mimeType: 'audio/red', muted: false},
{type: 'AUDIO', source: 'SCREEN_SHARE_AUDIO', mimeType: 'audio/red', muted: false},
],
},
{
identity: config.secondaryPublisherIdentity,
tracks: [
{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false},
{type: 'AUDIO', source: 'MICROPHONE', mimeType: 'audio/red', muted: false},
{type: 'AUDIO', source: 'SCREEN_SHARE_AUDIO', mimeType: 'audio/red', muted: false},
],
},
],
error: 'RoomService.ListParticipants failed with HTTP 401',
});
assert.equal(config.validateServerPublishing, true);
assert.equal(findCheck(checks, 'server publishing API participants listed').pass, false);
assert.equal(
checks.every((check) => check.pass),
false,
);
});
});
});