diff --git a/docs/status.md b/docs/status.md index 499c9a7..1b4d382 100644 --- a/docs/status.md +++ b/docs/status.md @@ -57,6 +57,7 @@ Flagship first; parallelize design/research/code/tests/audit. Ship minimum runna - PR #49/#50 BWE is unverified/default-off: test zero-child leaves, then root impact. No local per-leaf UDP shaper; resume with Linux `tc` or public canary, never CDP. Web P2P/SVC shortcuts remain no-go. - The corrected standby smoke is localhost/headless/video-only; public DNS/TLS reuse, transport, audio, shaping, load, mobile, endurance, and sub-25 ms overlap remain open. - Headful fragment-to-sessionStorage consumption, invitation rotation after migration, and request/log leak inspection remain production UX gates. Accounts stay out. +- Structure debt: split `HybridMediaRouter`, `HostPage`, `SignalingServer`, and `ViewerPage` only at proven consumer boundaries, never by file length. ## Next Milestone @@ -80,15 +81,12 @@ ADR-0004 still requires a full-resolution 30-minute `1/3/5/8` network, resource, quality, latency, recovery, and browser/mobile-leaf matrix. A separate 20-viewer gate must pass before the current default eight changes to target 20. -ADR-0004 remains fail-closed; Native/striping experiments cannot relabel it. - ## Blockers And Decisions ADR-0004/0005 remain No-Go for broad rollout. DNS/TLS, independent secrets, host/provider UDP 7882 and bounded services are deployed, but real external media/device evidence is absent. Shared IP remains smoke-only and cannot approve clean-port migration; the full gate still needs an isolated VM/IP. -Deferred architecture audit: `docs/maintenance.md`. - Whole-system versus selected-game audio for the first release. - Initial deployment region and network cohort. diff --git a/src/server/signaling.ts b/src/server/signaling.ts index 5b6781f..c872c5f 100644 --- a/src/server/signaling.ts +++ b/src/server/signaling.ts @@ -458,7 +458,7 @@ export class SignalingServer { }; let hybridState; try { - hybridState = this.isPeerAssistedRoom(participant.roomId) + hybridState = this.isHybridMediaEnabled() ? this.hybridMediaRouter!.connectParticipant(routeParticipant) : undefined; } catch { @@ -538,14 +538,14 @@ export class SignalingServer { if (participant.role === "host") { for (const viewer of connectedViewers) { this.sendToSession(viewer.sessionId, { type: "host-status", online: true }); - if (!this.isPeerAssistedRoom(participant.roomId)) { + if (!this.isHybridMediaEnabled()) { this.send(socket, { type: "peer-joined", peerId: viewer.peerId }); } } return; } - if (this.isPeerAssistedRoom(participant.roomId)) { + if (this.isHybridMediaEnabled()) { return; } @@ -576,7 +576,7 @@ export class SignalingServer { this.sendError(socket, "FORBIDDEN", "Only viewers may request an ICE restart"); return; } - if (this.isPeerAssistedRoom(authenticated.roomId)) { + if (this.isHybridMediaEnabled()) { this.routePeerAssistedRestart(socket, authenticated, message); return; } @@ -593,7 +593,7 @@ export class SignalingServer { return; case "set-quality-settings": if ( - !this.isPeerAssistedRoom(authenticated.roomId) || + !this.isHybridMediaEnabled() || authenticated.role !== "host" ) { this.sendError( @@ -618,7 +618,7 @@ export class SignalingServer { return; case "relay-capacity": if ( - !this.isPeerAssistedRoom(authenticated.roomId) || + !this.isHybridMediaEnabled() || authenticated.role !== "viewer" ) { this.sendError( @@ -637,7 +637,7 @@ export class SignalingServer { ); return; case "route-ready": - if (!this.isPeerAssistedRoom(authenticated.roomId)) { + if (!this.isHybridMediaEnabled()) { this.sendError(socket, "FORBIDDEN", "Media routes are not enabled"); return; } @@ -650,7 +650,7 @@ export class SignalingServer { ); return; case "route-failed": - if (!this.isPeerAssistedRoom(authenticated.roomId)) { + if (!this.isHybridMediaEnabled()) { this.sendError(socket, "FORBIDDEN", "Media routes are not enabled"); return; } @@ -665,7 +665,7 @@ export class SignalingServer { case "refresh-sfu": if ( !this.options.sfuFallback || - !this.isPeerAssistedRoom(authenticated.roomId) + !this.isHybridMediaEnabled() ) { this.sendError(socket, "FORBIDDEN", "SFU fallback is not enabled"); return; @@ -816,7 +816,7 @@ export class SignalingServer { }); } - if (this.isPeerAssistedRoom(authenticated.roomId)) { + if (this.isHybridMediaEnabled()) { for (const viewer of update.revokedViewers) { this.hybridMediaRouter!.removeViewer( authenticated.roomId, @@ -918,7 +918,7 @@ export class SignalingServer { let routeRevision = 0; let parent: ConnectedPeer | undefined; - if (this.isPeerAssistedRoom(source.roomId)) { + if (this.isHybridMediaEnabled()) { const edge = this.hybridMediaRouter?.resolveActivePeerEdge( source.roomId, source.peerId, @@ -987,7 +987,7 @@ export class SignalingServer { acceptedAtMs: now, parentEvidenceAccepted: false, }); - if (this.isPeerAssistedRoom(source.roomId)) { + if (this.isHybridMediaEnabled()) { this.hybridMediaRouter!.handleViewerQualityEvidence({ roomId: source.roomId, viewerSessionId: viewerState.sessionId, @@ -1005,7 +1005,7 @@ export class SignalingServer { { type: "parent-edge-quality-evidence" } >, ): void { - if (!this.isPeerAssistedRoom(source.roomId)) { + if (!this.isHybridMediaEnabled()) { return; } const sourceState = this.socketStates.get(socket); @@ -1060,7 +1060,7 @@ export class SignalingServer { source: AuthenticatedSession, message: Extract, ): void { - if (this.isPeerAssistedRoom(source.roomId)) { + if (this.isHybridMediaEnabled()) { this.routePeerAssistedSignal(sourceSocket, source, message); return; } @@ -1264,7 +1264,7 @@ export class SignalingServer { if (!disconnected) { return; } - if (this.isPeerAssistedRoom(disconnected.roomId)) { + if (this.isHybridMediaEnabled()) { this.hybridMediaRouter!.disconnectParticipant( disconnected.roomId, disconnected.peerId, @@ -1295,7 +1295,7 @@ export class SignalingServer { disconnected.roomId, disconnected.peerId, ); - if (this.isPeerAssistedRoom(disconnected.roomId)) { + if (this.isHybridMediaEnabled()) { this.hybridMediaRouter!.removeViewer( disconnected.roomId, disconnected.peerId, @@ -1317,7 +1317,7 @@ export class SignalingServer { private stopSharing(roomId: string): void { this.clearRoomConnectionIds(roomId); - if (this.isPeerAssistedRoom(roomId)) { + if (this.isHybridMediaEnabled()) { this.hybridMediaRouter!.stopRoom(roomId); } for (const viewer of this.options.roomStore.getConnectedViewers(roomId)) { @@ -1333,7 +1333,7 @@ export class SignalingServer { } this.clearRoomGraceTimers(roomId); this.clearRoomConnectionIds(roomId); - if (this.isPeerAssistedRoom(roomId)) { + if (this.isHybridMediaEnabled()) { this.hybridMediaRouter!.deleteRoom(roomId); } this.qualitySettingsByRoom.delete(roomId); @@ -1352,7 +1352,7 @@ export class SignalingServer { for (const expired of this.options.roomStore.expireRooms(this.now())) { this.clearRoomGraceTimers(expired.roomId); this.clearRoomConnectionIds(expired.roomId); - if (this.isPeerAssistedRoom(expired.roomId)) { + if (this.isHybridMediaEnabled()) { this.hybridMediaRouter!.deleteRoom(expired.roomId); } this.qualitySettingsByRoom.delete(expired.roomId); @@ -1406,10 +1406,7 @@ export class SignalingServer { } } - private isPeerAssistedRoom(roomId: string): boolean { - // The controller owns independent per-room state; the process flag is the - // only rollout switch. Keep the room argument at call sites for clarity. - void roomId; + private isHybridMediaEnabled(): boolean { return this.hybridMediaRouter !== undefined; } @@ -1487,7 +1484,7 @@ export class SignalingServer { role: "viewer", peerId: viewer.peerId, displayName: viewerState.displayName, - mediaTopology: this.isPeerAssistedRoom(roomId) + mediaTopology: this.isHybridMediaEnabled() ? this.hybridMediaRouter!.getViewerMediaTopology( roomId, viewer.peerId, diff --git a/src/shared/protocol.ts b/src/shared/protocol.ts index 07d5e7e..2b49900 100644 --- a/src/shared/protocol.ts +++ b/src/shared/protocol.ts @@ -114,7 +114,6 @@ export const viewerPasswordSchema = z .min(MIN_VIEWER_PASSWORD_LENGTH) .max(MAX_VIEWER_PASSWORD_LENGTH) .regex(/^[\x21-\x7e]+$/); -export type ViewerPassword = z.infer; export const viewerAccessPolicySchema = z.enum([ "private-link", @@ -150,7 +149,6 @@ export const qualityProfileIdSchema = z.enum([ "720p30", ]); export type QualityProfileId = z.infer; -export const DEFAULT_QUALITY_PROFILE_ID: QualityProfileId = "1080p60"; export const qualityResolutionSchema = z.enum(["720p", "1080p", "1440p"]); export type QualityResolution = z.infer; @@ -446,9 +444,6 @@ export const viewerQualityEvidenceMessageSchema = z .refine(freezeFitsEvidenceWindow, { message: "Viewer freeze duration exceeds its evidence window", }); -export type ViewerQualityEvidenceMessage = z.infer< - typeof viewerQualityEvidenceMessageSchema ->; const parentEdgeQualityProofSchema = z.discriminatedUnion("kind", [ z @@ -485,9 +480,6 @@ export const parentEdgeQualityEvidenceMessageSchema = z proof: parentEdgeQualityProofSchema, }) .strict(); -export type ParentEdgeQualityEvidenceMessage = z.infer< - typeof parentEdgeQualityEvidenceMessageSchema ->; const authenticateMessageSchema = z.discriminatedUnion("role", [ z @@ -824,7 +816,6 @@ export const createRoomRequestSchema = z hostClaimTtlSeconds: z.literal(MAX_HOST_CLAIM_TTL_SECONDS).optional(), }) .strict(); -export type CreateRoomRequest = z.infer; export function decodeClientMessage(value: string): ClientMessage { return clientMessageSchema.parse(JSON.parse(value));