You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The #1134 train shipped as eight PRs (#1136–#1143). This is everything that audit surfaced and did not close, in one place, so the tail does not get lost when the train merges.
Each item is marked with how it was established: [live] read from the running Stream app, [code] verified against dev, [audit] carried from #1134 and not re-checked.
1. Never written — the two PRs that were planned and skipped
PR 9 · P3 cleanup
[live] SDK upgrades. Installed versions run ahead of what package.json implies: @stream-io/video-react-sdk 1.35.2 (latest 1.40.2), stream-chat 9.41.1 (9.50.3), stream-chat-react13.14.4 (14.11.x — a MAJOR, needs its own PR), @stream-io/node-sdk 0.7.54 (0.7.63).
[code] Dead schema. MeetingSession.passcode and MeetingSession.hostKeys are written by the seeds and read by nothing. Class.recordingUrls bypasses the Recording model entirely.
[code]MeetingAttendance is never seeded, so every surface that reads it renders empty locally.
PR 10 · Docs
docs/stream/README.md does not exist but is linked from three files.
Zero occurrences of "BetterAuth" across docs/stream/, while five files still describe NextAuth.
05-video-implementation.md documents a streamCallId format that has not been used for months.
13-recording-webhooks.md declares a StorageType enum that does not exist.
Three different names for the org tag across three documents.
2. Open findings
2a. call_member can end a call for everyone — new, found during #1136 review triage
[live] The default call type grants call_member the end-call permission, and POST /api/meetings/[meetingId]/join assigns call_member to every participant. Any attendee can end a consultation for both sides from devtools.
ensure-call-type-grants.ts deliberately does not revoke it: EndCallButton.tsx:46 calls call.endCall() client-side, and the Stream role no longer separates host from participant (host-ness comes from custom.consultantUserId). Revoking it today would take the host's End Call button down with it.
Closing it needs one of:
a server-side End Call route that re-checks host-ness via resolveMeetingAccess, then revoke the grant; or
assigning admin (a real role on this type, 40 permissions) to access.role === "host" and call_member to everyone else, then revoke.
The same PR did revoke start-recording/stop-recording from call_member, because recording is server-only here — there is no client-side call.startRecording() in the tree — and leaving it granted made the pre-join consent gate from #1139 bypassable.
2b. No unban path calls restoreStreamAccess — new, found during #1136 review triage
[code]restoreStreamAccess() exists and has zero callers. A USER_BANNED action calls deactivateUser, which is permanent; if an operator lifts a permanent ban, the account is left unable to chat with no visible cause and nothing in the codebase undoes it.
#1136 narrowed the function's catch so a timeout or 5xx no longer resolves as a successful restore, but building the reversal path itself is a feature, not a review fix. Note that Stream documents neither outcome for reactivateUser on an already-active user, so the narrow match is asserted rather than documented — worth confirming against the live app when this is built.
2c. sendCallEvent has no subscriber
[audit] There is no call.on("custom") handler, so the notified count measures API acceptances rather than humans warned. Item 4 of #1134's "found wrong and not yet fixed" list, and the only one of those four still open.
2d. The remove-member control has never rendered for anyone
[code]lib/user.ts:82mapRoleToStream collapses every non-staff account to Stream's "user" role — correct, it is the #981 least-privilege fix. But components/chat/ChannelInfoAndManageDialog.tsx:93 gates ownership on client?.user?.role === "CONSULTANT", an app role client.user.role can never hold. isEventOwner is permanently false and the button at :610 is dead.
Product decision needed — who may remove a member from a webinar/class channel? Plan owner only / owner + accepted collaborators (matching resolveMeetingAccess) / staff+admin only. Whichever it is, the check must read the session, never client.user.role.
Generalises: any comparison of client.user.role against an app role (CONSULTANT/CONSULTEE/STAFF) is dead code by construction. Worth grepping for. The identical bug in ChatSidebar is already fixed in #1142.
3. Missed by the audit — the highest-value unbuilt item
Setting it makes the SFU end the call server-side at the slot boundary, so Stream stamps ended_at whether or not our webhook pipeline works. Given the audit found 1,417 orphaned sessions and a webhook pipeline that had never processed a single event, this is the one control that would have degraded gracefully through the entire outage. Stream's own worked example for it is a telemedicine app with extendable one-hour appointments.
lib/meeting.ts explicitly declines to send it, deferring to #1070. That deferral is worth revisiting. Overlaps #472 (session overrun detection), which describes the same problem from the application side.
Background images are wired but not exposed — needs a curated asset set.
No setSuppressionLevel slider for noise cancellation.
[live] Correction worth carrying:#1134's comment thread states the call type "already advertises noise_cancellation.mode: auto-on". It does not — settings.noise_cancellation reads back undefined on the live default type. This changes the billing risk framing for shipping @stream-io/audio-filters-web: the exposure is real but it is not already switched on. Confirm live before acting either way.
The #1134 train shipped as eight PRs (#1136–#1143). This is everything that audit surfaced and did not close, in one place, so the tail does not get lost when the train merges.
Each item is marked with how it was established: [live] read from the running Stream app, [code] verified against
dev, [audit] carried from #1134 and not re-checked.1. Never written — the two PRs that were planned and skipped
PR 9 · P3 cleanup
package.jsonimplies:@stream-io/video-react-sdk1.35.2 (latest 1.40.2),stream-chat9.41.1 (9.50.3),stream-chat-react13.14.4 (14.11.x — a MAJOR, needs its own PR),@stream-io/node-sdk0.7.54 (0.7.63).MeetingSession.passcodeandMeetingSession.hostKeysare written by the seeds and read by nothing.Class.recordingUrlsbypasses theRecordingmodel entirely.MeetingAttendanceis never seeded, so every surface that reads it renders empty locally.PR 10 · Docs
docs/stream/README.mddoes not exist but is linked from three files.docs/stream/, while five files still describe NextAuth.05-video-implementation.mddocuments astreamCallIdformat that has not been used for months.13-recording-webhooks.mddeclares aStorageTypeenum that does not exist.2. Open findings
2a.
call_membercan end a call for everyone — new, found during #1136 review triage[live] The
defaultcall type grantscall_membertheend-callpermission, andPOST /api/meetings/[meetingId]/joinassignscall_memberto every participant. Any attendee can end a consultation for both sides from devtools.ensure-call-type-grants.tsdeliberately does not revoke it:EndCallButton.tsx:46callscall.endCall()client-side, and the Stream role no longer separates host from participant (host-ness comes fromcustom.consultantUserId). Revoking it today would take the host's End Call button down with it.Closing it needs one of:
resolveMeetingAccess, then revoke the grant; oradmin(a real role on this type, 40 permissions) toaccess.role === "host"andcall_memberto everyone else, then revoke.The same PR did revoke
start-recording/stop-recordingfromcall_member, because recording is server-only here — there is no client-sidecall.startRecording()in the tree — and leaving it granted made the pre-join consent gate from #1139 bypassable.2b. No unban path calls
restoreStreamAccess— new, found during #1136 review triage[code]
restoreStreamAccess()exists and has zero callers. AUSER_BANNEDaction callsdeactivateUser, which is permanent; if an operator lifts a permanent ban, the account is left unable to chat with no visible cause and nothing in the codebase undoes it.#1136 narrowed the function's catch so a timeout or 5xx no longer resolves as a successful restore, but building the reversal path itself is a feature, not a review fix. Note that Stream documents neither outcome for
reactivateUseron an already-active user, so the narrow match is asserted rather than documented — worth confirming against the live app when this is built.2c.
sendCallEventhas no subscriber[audit] There is no
call.on("custom")handler, so thenotifiedcount measures API acceptances rather than humans warned. Item 4 of #1134's "found wrong and not yet fixed" list, and the only one of those four still open.2d. The remove-member control has never rendered for anyone
[code]
lib/user.ts:82mapRoleToStreamcollapses every non-staff account to Stream's"user"role — correct, it is the #981 least-privilege fix. Butcomponents/chat/ChannelInfoAndManageDialog.tsx:93gates ownership onclient?.user?.role === "CONSULTANT", an app roleclient.user.rolecan never hold.isEventOwneris permanentlyfalseand the button at:610is dead.Product decision needed — who may remove a member from a webinar/class channel? Plan owner only / owner + accepted collaborators (matching
resolveMeetingAccess) / staff+admin only. Whichever it is, the check must read the session, neverclient.user.role.Generalises: any comparison of
client.user.roleagainst an app role (CONSULTANT/CONSULTEE/STAFF) is dead code by construction. Worth grepping for. The identical bug inChatSidebaris already fixed in #1142.3. Missed by the audit — the highest-value unbuilt item
Session Timers (
settings.limits.max_duration_seconds)[live] Currently
nullon thedefaultcall type.Setting it makes the SFU end the call server-side at the slot boundary, so Stream stamps
ended_atwhether or not our webhook pipeline works. Given the audit found 1,417 orphaned sessions and a webhook pipeline that had never processed a single event, this is the one control that would have degraded gracefully through the entire outage. Stream's own worked example for it is a telemedicine app with extendable one-hour appointments.lib/meeting.tsexplicitly declines to send it, deferring to #1070. That deferral is worth revisiting. Overlaps #472 (session overrun detection), which describes the same problem from the application side.4. Deferred by #1143, stated in its body
setSuppressionLevelslider for noise cancellation.[live] Correction worth carrying: #1134's comment thread states the call type "already advertises
noise_cancellation.mode: auto-on". It does not —settings.noise_cancellationreads backundefinedon the livedefaulttype. This changes the billing risk framing for shipping@stream-io/audio-filters-web: the exposure is real but it is not already switched on. Confirm live before acting either way.Not in here
Part of #1134