Commit Graph
3357 Commits
Author SHA1 Message Date
Sumit Kumar 9dc21a2ed5 whip: stop waiting for subscribed tracks when the request ends (#5155)
whipService.Create waits for every track listed in
SubscribedParticipantTracks. The errgroup context was discarded and each
goroutine only exited once its track was subscribed, so a track that
never appears (a wrong name, a publisher that left) kept the request and
a polling goroutine per track running forever, long after the caller
had timed out.

Move the wait into waitForSubscribedTracks and have it return when the
request context is done or the participant has closed.

Fixes #5152
2026-11-07 11:14:05 +08:00
Raphael Fakhri 94edd5da75 sfu: find the RTX payload type when its fmtp line has more than apt (#5135)
FindRTXPayloadType compared the whole fmtp line with "apt=<pt>", so an
RTX codec negotiated as "apt=96;rtx-time=3000" (RFC 4588) was never
found. The down track then stored RTX payload type 0, sent
retransmissions on the primary SSRC and probed with padding even though
RTX was negotiated.

Compare only the apt parameter of the fmtp line.
2026-11-06 15:54:32 +05:30
Anunay Maheshwari 6087e6bef5 fix(rtc): don't miss publishing tracks on resume, and rewrite the opus answer fmtp by key (#5148)
* fix(rtc): look up pending before published tracks on resume so a moving track is not missed

* fix(rtc): rewrite the opus answer fmtp by key and drop the line when nothing remains

* chore(rtc): note why a track closed between the resume lookups reads as unknown

* test(rtc): keep the parked reader helper beside its only user in room_test

* fix(rtc): match the opus fmtp by exact payload type in the publisher answer
2026-11-06 15:41:27 +05:30
2d4bd8af55 rtc: disable transceiver reuse for all WebKit clients, incl. iOS (#5133)
* rtc: disable transceiver reuse for all WebKit clients, incl. iOS

#4474 turned off transceiver reuse on Safari because of an insertable
streams issue that can corrupt E2EE tracks. The check matched only a
browser name of exactly "Safari". The server's user agent parser reports
iPhone Safari as "Mobile Safari", WKWebView as "Mobile Safari
UI/WKWebView", and other iOS browsers as "Chrome Mobile iOS",
"Firefox iOS", "Edge Mobile" or an app name. All of these run on WebKit,
but they kept transceiver reuse.

Match any browser name that contains "safari", and any JS SDK client on
iOS. React Native, Swift and Flutter do not match. isSafari() and audio
RED are not changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* rtc: reuse isSafari() in isWebKit()

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* rtc: use isWebKit() for audio RED support too

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: lukasIO <mail@lukasseiler.de>
2026-11-06 11:05:13 +01:00
Anunay Maheshwari beee7b0eaa fix(rtc): hold pendingTracksLock across both track lookups in mute and track updates (#5147)
* test(rtc): cover mute and track updates while a track moves to published

* fix(rtc): hold pendingTracksLock across both track lookups in mute and track updates

* fix(rtc): update queued replacements before the published track in mute and track updates
2026-11-06 12:55:44 +05:30
Anunay Maheshwari 0dd174b3ed fix(rtc): hold pendingTracksLock across both track lookups when configuring the publisher answer (#5146)
* test(rtc): cover the publisher answer while its track moves to published

* fix(rtc): hold pendingTracksLock across both track lookups when configuring the publisher answer
2026-11-06 03:23:26 +05:30
Théo Monnom 4d88d3dc06 agent endpoints: forward the caller's token to the worker (#5145)
* agent endpoints: forward the caller's token to the worker

* agent endpoints: the chain test expects the query token forwarded
2026-11-05 11:21:56 -08:00
cnderrauber 22ba651703 Pick DTLS server role when ice-lite is enabled (#5143)
To speed up data channel and warp. Exclude sdks (unknown ||
esp32) that may have problem to support answer dtls server role.
2026-11-05 22:01:40 +08:00
cnderrauber e269e3e355 Update webrtc fork to dtls1.3 (#5142)
* Update webrtc fork to dtls1.3

* update dep

* Remove debug line

* Fix test
2026-11-05 18:20:18 +08:00
Genseric 988e6f675e sip: normalize inbound trunk numbers before storing (#5129)
Stores that filter trunks by exact number, such as the Cloud database,
only normalize the called number, so a stored 15550001002 never matches
a call to +15550001002. Normalize numbers on create and update, including
the stored list and the Remove values, so that old rows are fixed on edit.
2026-11-04 13:46:03 -05:00
Raja SubramanianandClaude Opus 5.5 77eeb22b62 agent: stamp the accept and assignment times of a job attempt (#5123)
* agent: stamp the accept and assignment times of a job attempt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* deps: pin protocol with the job accept and assignment times

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* service: stamp a new user's join request time as an attribute

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* service: use the lk.rtc.joinRequestTime name

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* deps: pin protocol with the join request time attribute

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* deps: pin protocol main, keep turn and srtp on pion/transport v4

The warp forks of webrtc, dtls and ice are still on pion/transport v4.
Protocol main requires turn 5.1 and srtp 3.1, which moved to v5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* deps: pin protocol v1.53.2

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: expect the join request time among a participant's attributes

The server now stamps lk.rtc.joinRequestTime on a new standard participant, so
other participants see it beside the token's attributes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 20:54:48 +05:30
armaan4658 7da670eef7 rtc: read subscriber track settings under the lock in applySettings (#5126)
applySettings releases settingsLock before it computes the video layers,
then reads t.settings.Quality, Width, Height and Fps without the lock.
UpdateSubscriberSettings replaces t.settings under the lock, and the
debouncer and UpdateVideoLayer run applySettings on other goroutines, so
the reads race with the write. go test -race reports it on the pointer
and on the fields of the newly cloned settings.

Take the settings pointer while holding the lock and compute the layers
from that snapshot. UpdateSubscriberSettings always stores a fresh clone
and never mutates it, so the snapshot is safe to read after unlocking,
and all four values come from the same update.
2026-11-03 16:17:17 +05:30
cnderrauber 4cbf2d3ebd Shrink NALU size when stripping h264/h265 aggregation packet trailer (#5125)
Fix #5082
2026-11-03 16:17:33 +08:00
lukasIOandClaude Opus 5.5 0d762b4e50 rtc: match publisher m-sections by mid, not by position (#5122)
* rtc: match publisher m-sections by mid, not by position

A client that stops a transceiver lets the next one recycle its m-line
(JSEP 5.2.2), so a new m-section can come before negotiated ones.
GetUnmatchMediaForOffer stopped at the last answered mid and missed it,
so its codec preferences were never applied.

Bump the protocol so clients know they can stop transceivers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Update protocol_version.go

* Remove comments from GetUnmatchMediaForOffer function

Removed comments explaining the GetUnmatchMediaForOffer function.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 17:20:11 +01:00
Raja Subramanian 13b23fa722 Use Start on ReceiverBase to ensure struct is fully set up before (#5121)
callbacks.
2026-11-02 20:08:34 +05:30
armaan4658andClaude Opus 5.5 b7af480c2d rtc: reject AddTrack with a repeated video layer quality (#5115)
ValidVideoLayers checks that each layer's quality is in range but lets a
quality repeat. GetQualityForDimension uses the index into the sorted
layer sizes as the quality, so a list such as LOW, LOW, MEDIUM, HIGH maps
the largest size to index 3, which is OFF, and a subscriber asking for
that size receives no video.

Reject an AddTrack whose layer list repeats a quality. Each list (the
request's and each simulcast codec's) is checked on its own, so the same
qualities on the request and a codec are still accepted. With only three
qualities this also bounds a list to three layers.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 14:03:42 +05:30
Raja SubramanianandClaude Opus 5.5 94b5e9a4e5 rtc: stop TestPreferAudioCodecForRed from hanging on a stalled ICE write (#5119)
The test client pc gathered host candidates on every interface. On a
host with a network content filter, a STUN binding request can block
in sendto forever. The write runs inside the ICE agent taskloop, so
SetRemoteDescription and the next CreateOffer wait on that loop and
the package hits its timeout.

The test checks only SDP, so the client pc now gathers no host
candidates and neither agent sends a connectivity check.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 13:03:03 +05:30
Raja SubramanianandClaude Opus 5.5 8eb162f104 rtc: do not advertise a subscribed track the subscriber cannot decode (#5117)
* rtc: do not advertise a subscribed track the subscriber cannot decode

In single peer connection mode a subscriber offers a recvonly section with
its own codecs. If the published track uses a codec the subscriber did not
offer, DownTrack.Bind finds no match but returns nil, because returning an
error aborts the whole transport. pion then keeps the sender, so the answer
still carries the track msid and ssrc. The subscriber sees a short-lived
TrackSubscribed before the bind failure removes the track again.

Park such a sender before building the answer: drop its track so the
section answers inactive, then report the codec failure out of band so the
subscription manager unsubscribes without reentering negotiation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* rtc: report codec mismatch via Handler, not the downtrack

Route the parked sender's failure through a new Handler method into the
participant, instead of reaching from transport into the downtrack. The
participant unsubscribes and reports the codec error. Keeps transport
free of the sfu package.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* rtc: use CodecsFromMediaDescription for the offered-codec check

Reuse the protocol sdp helper instead of hand-parsing rtpmap lines.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* rtc: reword codec-suppression comments

Describe it as a subscription, drop the stale prototype label.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* rtc: check every bindable codec before suppressing a sender

The check before the answer looked only at the primary codec. A subscriber
that offers a later upstream codec, for example Opus for a RED-first track,
lost a track that Bind can bind.

DownTrack.BindableCodecs returns the codecs Bind can pick, and both Bind and
the check use it. It also fixes the encrypted RED/Opus rule in Bind: the old
condition was never true, so an encrypted RED track could bind as Opus.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* rtc: keep published encrypted codecs bindable, record codec rejections

The encrypted RED/Opus rule cut every RED/Opus pair to the primary codec.
That also dropped a second codec the publisher sends on its own stream.
WrappedReceiver now decides which codecs bind: for an encrypted source,
only its published codecs, never the RED or Opus alternative it adds.

A sender parked before the answer now ends its subscription through the
same path as a bind error, so it records OnTrackSubscribeFailed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-11-02 12:32:28 +05:30
Raja SubramanianandClaude Fable 5.1 db2e810958 rtc: log the attributes of every data channel (#5116)
* rtc: log the attributes of every data channel

Both transports log "data channel open" at info with the channel's
label, id, ordered, maxRetransmits, maxPacketLifeTime, protocol and
negotiated, through one helper.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* paul feedback

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-01 11:29:19 +05:30
Raja SubramanianandClaude Opus 5.5 9a26932591 Harden media and room handling against bad inputs and races (#5114)
* sfu: ignore a nil publisher sender report in the forwarder

A stream restart replaces the receiver stats between the sender report
callback and its read, so the report can be nil. SetRefSenderReport now
keeps the previous report instead of reading the nil one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* dynacast: skip a codec regression when the target codec has no quality

HandleCodecRegression looked up the target codec after it released the
lock. If the manager closed in between, or the target mime type was
unknown, the lookup returned nil and RegressTo called Restart or Replace on
it. The target is now looked up with the source, and a missing one stops
the regression.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* service: create the internal record for a stored room that has none

RedisStore.LoadRoom returns a nil RoomInternal without an error when the
room exists but its internal field does not, for example after a delete
races a store. CreateRoom then wrote egress, agent, playout delay or sync
settings into the nil record. It now starts from an empty record, which
the store call writes back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* sfu: drop a dependency descriptor whose chains do not match the structure

The parser takes a new structure from any packet, but DDForwarder keeps
the structure of the last packet it forwarded. When the publisher attached
a structure with fewer chains to a packet this downtrack dropped, the next
descriptor had fewer chain diffs than the forwarder's structure, and the
writer indexed past them. The forwarder now drops such a packet, and the
writer returns an error instead of indexing past the chain diffs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* deps: bump mediatransportutil for the empty VP9 payload fix

A VP9 packet without a dependency descriptor and with a 1-byte payload such
as 0x08 parses to an empty VP9 payload, and IsVP9KeyFrame read its first
byte. mediatransportutil#106 returns false for an empty payload. This pins
the transport-v4 commit that carries it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* sfu: keep the template test comment with its test

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* dynacast: create the target quality only for a valid regression

A target quality starts a timer that notifies the publisher. Look it up
only after the source codec is found, so a rejected regression creates
nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* service: restore the internal defaults for a room that lost them

A stored room without an internal record now gets the configured playout
delay and sync streams defaults. Its own settings, such as the empty
timeout and codecs, stay as stored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 19:29:11 +05:30
Raja SubramanianandClaude Fable 5.1 adb7cb4f29 rtc: hold unlabeled data for a participant whose reliable channel is opening (#5113)
An unlabeled data message sent to a participant without an unlabeled
channel is converted to a UserPacket and written to the reliable
channel. That write failed while the channel was still opening, and the
message was lost. The participant now builds the UserPacket and sends
it through SendDataMessage, which holds unsequenced reliable data until
the channel is writable. The transport keeps only the raw path.

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-31 16:16:57 +05:30
Raja SubramanianandClaude Fable 5.1 6b172bfb4a rtc: broadcast an unlabeled data message from a source identity (#5112)
* rtc: broadcast an unlabeled data message from a source identity

BroadcastDataMessageForRoomFrom takes the source ID and identity instead
of a LocalParticipant, so a caller can stamp the sender of a message
whose source is not on this node. BroadcastDataMessageForRoom keeps its
signature and calls it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* rtc: say what sourceID does in the BroadcastDataMessageForRoomFrom comment

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-31 15:33:34 +05:30
Raja SubramanianandClaude Opus 5.5 1681f36610 rtc: guard nil source in BroadcastDataMessageForRoom (#5110)
Relayed unlabeled data arrives with no local source participant, and
the sender identity lookup dereferenced it.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 13:16:19 +05:30
David Zhao 09a0091e9d chore: add JobAttempt helper (#5109)
renamed a few fields for consistency
2026-10-30 22:08:07 -07:00
David Zhao 8f7597e0ca include agent's jobID & dispatch attempt in agent token (#5103)
* include agent's jobID & dispatch attempt in agent token

* better handle clock skew

* rename to lk.agent. namespace
2026-10-30 18:36:13 -07:00
Raja SubramanianandClaude Opus 4.8 996b5bfb9a rtc: reject AddTrack with an out-of-range video layer mode (#5106)
A client sets SimulcastCodec.VideoLayerMode on AddTrack. Any value other
than MODE_UNUSED is used as-is, so a forged out-of-range value flows into
the forwarder. Reject it in ValidVideoLayers, alongside the quality check.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-30 20:37:35 +05:30
Raja Subramanian ebc71a6d11 rtc: reject AddTrack with an out-of-range video layer quality (#5102) 2026-10-30 02:16:04 +05:30
Martin Njalale 63ad72da9d service: send queued webhooks before shutdown completes (#5090)
* service: send queued webhooks before shutdown completes

Start returned, and the process exited, while the room_finished webhooks
of the rooms closed by the shutdown were still queued. Stop the telemetry
service at the end of Start so it flushes its queue and drains the webhook
notifier, waiting up to 5s.

* Bump protocol to pick up the ResourceURLNotifier drain fix

ResourceURLNotifier.Stop(false) now waits for the queued events to be sent (livekit/protocol#1901), which the shutdown change relies on.

* telemetry: keep the analytics of webhooks sent during stop

The notifier's processed hook queues a WEBHOOK analytics event for each webhook it sends, but Stop stopped the jobs queue before draining the notifier, so those events were dropped. Run the queued events with a barrier, drain the notifier, then stop the queue.
2026-10-29 23:51:29 +05:30
Raja SubramanianandClaude Opus 5.5 2c8c97ee25 agent: fix flaky TestDrainConnectionsDoesNotDeadlock (#5096)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 11:23:23 +05:30
Raja SubramanianandClaude Opus 5.5 0ddbefdf8c Add node_stats.host_cpu to measure CPU load for the whole host (#5095)
* Pass CPU stats options through prometheus.Init

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Add node_stats.host_cpu config to measure whole-host CPU

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Bump protocol to ignore the CPU request with host CPU stats

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 11:02:39 +05:30
GrantandClaude Opus 5.5 d5f5bcb60c sfu: resync dependency descriptor frame numbers on unmute (#5094)
On mute the forwarder resyncs sequence numbers, timestamps and VP8 picture
IDs, but DDForwarder kept its frame number offset. libwebrtc counts DD
frames across all simulcast layers, so after a long mute the frame number
sent on unmute jumped by more than half the 16 bit range. The subscriber
took every frame as older than the last decoded one and dropped it, a
black tile until the numbers wrapped past it again.

Resync DDForwarder with the forwarder, and move the numbers to just after
the last one sent when they jump too far ahead.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 10:52:36 +05:30
Raja SubramanianandClaude Opus 5.5 4a2615f127 sfu: throttle PLIs with an atomic before taking the rtpstats lock (#5093)
* Throttle PLIs with an atomic before taking the rtpstats lock

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Reset the PLI throttle when the stream restarts

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 00:34:28 +05:30
Raja SubramanianandClaude Opus 5.5 e0e34aba8f Take the read lock in GetOrCreateBuffer when the buffer exists (#5092)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 00:29:03 +05:30
Abhishek B R 5874f2f569 service: take the client from the first X-Forwarded-For entry (#5091)
GetClientIP returned the whole X-Forwarded-For header, so a request that
passed through more than one proxy set ClientInfo.Address to a list like
"198.51.100.2, 10.0.0.5" instead of an IP. Each proxy appends to the
header, so the client is the first entry.
2026-10-28 10:42:34 -07:00
Paul Wells c3605dfa6c Sign refresh tokens with the join token's API key (#5088)
Refresh tokens were signed with the first configured key pair, which is
an arbitrary map entry when several keys are configured. Keep the join
token's API key on the participant and sign with it when its secret is
still configured, falling back to the first key pair otherwise.
2026-10-28 04:52:48 -07:00
Raja SubramanianandClaude Opus 5.5 2f7337b759 rtc: ignore server-owned video layer fields in AddTrack (#5087)
The server sets the spatial layer, rid, ssrc and repair ssrc of each
video layer from SDP and RTP. Clear these fields on AddTrack request
layers, including each simulcast codec's layers.

Clamp the layer in ReceiverBase.SetMaxExpectedSpatialLayer to
[InvalidLayerSpatial, DefaultMaxLayerSpatial].

Keep a client set video layer mode, but warn when it differs from the
server determined mode.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 16:23:52 +05:30
Abhishek B R 428edee0bf config: reject out-of-range values in generated numeric flags (#5022)
* config: reject out-of-range values in generated numeric flags

Generated flags for uint8, uint16, uint32, int32 and float32 config
fields were parsed at a wider type and then converted with
reflect.Value.Convert, which wraps silently. For example
LIVEKIT_TURN_RELAY_RANGE_START=70000 became 4464, and
LIVEKIT_LIMIT_MAX_METADATA_SIZE=4294967296 became 0, which turns the
limit off. The YAML config rejects the same values.

Use the cli flag type that matches each field's width so the parse
fails with the env var or flag name in the error.

* config: cover uint, int8 and int16 in generated numeric flags

Move the numeric cases into numericCLIFlag and add Int8Flag, Int16Flag and UintFlag, so every numeric kind gets a flag of its own width even though no config field uses these three yet. TestNumericCLIFlagWidths checks each kind directly with an in-range value and one that does not fit.

* config: return an error from numericCLIFlag for unsupported kinds

numericCLIFlag returned nil when no case matched, and GenerateCLIFlags appended it unchecked. It now returns (cli.Flag, error) and the caller returns the error like the default case does.
2026-10-28 11:28:29 +05:30
Elliot Morales Solé 6db4a7ab8c test: fix TestEgressStore updating the wrong egress info (#5078)
The test mutates info2 (room "another-egress-test") to COMPLETE with an
EndedAt 24h in the past, then persists `info` instead of `info2`. So info2
stays STARTING forever, CleanEndedEgress never removes it, and the record
leaks into whatever Redis the suite ran against.

The leak is invisible because the final assertion only lists the primary
room. It surfaces on the NEXT run: utils_test.go:49 prefers a reachable
localhost:6379 over an ephemeral dockertest container, so against a
persistent Redis the list-all assertion grows by one record per run and
TestEgressStore starts failing with "should have 2 item(s), but has 3".

With info2 persisted, CleanEndedEgress removes both records and the test
cleans up after itself. Verified: 3 consecutive runs green, zero keys left.
2026-10-27 16:27:34 +01:00
cnderrauber 37901309a3 Revert #5033 (#5077)
WHIP passthrough ingress has been failing DTLS
2026-10-27 21:04:38 +08:00
Hassan Kazi 953b489987 service: check room name length in CreateDispatch (#5071)
With auto_create on, CreateDispatch creates the room through router.CreateRoom without checking limits.max_room_name_length, so it could create a room that CreateRoom and the join paths reject.
2026-10-27 00:56:36 -07:00
Raja SubramanianandClaude Opus 5.5 d2cf39bce6 sfu: replace forward scheduling delay with processing per write (#5072)
* sfu: replace forward scheduling delay with processing per subscriber

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* sfu: describe processing per subscriber as per write

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* telemetry: name forward processing metrics per write

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Pin protocol with NodeStats forward_processing_per_write

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 01:52:17 +05:30
Daisuke Iuchi b50c7f09cf Return an error when the ingress store exhausts its retries; tolerate missing state in UpdateIngress/DeleteIngress (#5049)
- pkg/service/redisstore.go: after the retry loop, return an error wrapping redis.TxFailedErr instead of nil. CreateIngress now fails instead of returning an ingress that was never written, StoreIngress stops before creating the orphan state key, and a dropped UpdateIngressState reaches the caller's existing error log. 

- pkg/service/ingress.go: the Fix nil pointer dereference in IOInfoService.UpdateIngressState on concurrent DeleteIngress #4806 guard applied to the two Twirp handlers, treating a nil State as empty. UpdateIngress then applies and stores the request; DeleteIngress deletes without calling the ingress service.

- pkg/service/redisstore.go: UpdateIngress now fails with ErrIngressNotFound when the ingress no longer exists, checked inside the WATCH on the ingress hash so a later delete fails the EXEC. Without it an update racing a  DeleteIngress recreated the deleted ingress and its stream key; this was possible before this PR whenever the delete landed after LoadIngress, and the guard in 2 widened the window to the gap between its two reads.
The guard stays at the call sites to match #4806 and keep API responses unchanged.
2026-10-26 13:00:41 -07:00
David Zhao 213df1a634 chore: add AttachJob interface on Worker (#5069)
* chore: add AttachJob interface on Worker

allows for a worker to take ownership of an existing job

* ensure we can handle stateless jobs

* set initial state in AssignJob
2026-10-26 12:42:16 -07:00
Raja SubramanianandClaude Opus 5.5 c82f28d6cd service: export CheckConnectLimits (#5067)
Servers that embed pkg/service and run their own join paths need the
same join limit check. Rename checkConnectLimits to CheckConnectLimits.
No behavior change.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 17:30:10 +05:30
e01a473d04 service: consolidate join grant-limit checks on the validate path (#5066)
* service: enforce participant name length on join

StartSession checked the token's metadata and attributes against the configured limits but not its name, so a participant could join with a name longer than limits.max_participant_name_length, which UpdateParticipant and the signal UpdateMetadata path reject.

* service: consolidate join grant-limit checks on the validate path

Extract checkGrantLimits and run it from ValidateConnectRequest and
WHIPService.validateCreate so the /rtc/validate endpoint and the WHIP
ingest path reject over-limit name, metadata and attributes the same way
a real join does. StartSession keeps its own checks as the backstop.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* service: run the full join limit check in StartSession

StartSession now calls checkConnectLimits, so the RTC node checks
identity, room name, participant name, metadata and attributes itself.
ValidateConnectRequest runs the check after the publish suffix, so it
measures the same identity StartSession sees. checkConnectLimits returns
a plain error, and the HTTP callers map it to 400. The name-only
StartSession test is dropped for TestConnectLimits, which gains a
publish-suffix case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* service: test the StartSession limit check directly

TestStartSessionConnectLimits runs the same over-limit cases as
TestConnectLimits through StartSession on the RTC node. The cases move
into an exported ConnectLimitCases table so both test packages share it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: ANDROIDHASSAN <84630851+ANDROIDHASSAN@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-26 17:16:51 +05:30
Raja SubramanianandClaude Opus 5.5 125ea44a61 sfu: report forward scheduling delay next to forwarding latency (#5065)
* sfu: report forward scheduling delay next to forwarding latency

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* sfu: keep full forwarding transit when packing scheduling delay

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* sfu: saturate reported forwarding p90s instead of wrapping

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 15:24:31 +05:30
Hassan Kazi 043637d36f rtc: refuse WHIP publish to a missing room when auto_create is off (#5062)
The WHIP create handler never called ValidateCreateRoom, so with room.auto_create: false a publisher token without roomCreate could create any room over WHIP, while the same join over /rtc gets 404.
2026-10-26 14:32:43 +05:30
Raja Subramanian 6294ab37c7 prometheus: count keyframes received and forwarded (#5060) 2026-10-25 13:08:05 +05:30
Raja SubramanianandClaude Opus 5.5 7c575a5c31 sfu: cap retransmissions per down track (#5054)
* sfu: cap retransmissions per down track (rtc.rtx config, off by default)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* sfu: rtx budget review fixes (sequencer undo, sent bytes, newest first, rtx_budget)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* sfu: rtx budget second review (order unchanged when off, keep NACK window, metric in down track)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* sfu: rtx budget sorts NACKs newest first by extended sequence number

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 11:05:54 +05:30
Raja SubramanianandClaude Opus 5.5 04f7cb71e3 rtc: fix flaky TestSetPermissionDataTrackSubscriptions (#5056)
* rtc: wait for the recorded data track subscription before revoking in test

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* rtc: drop a subscription whose canSubscribe was revoked while it was in flight

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 11:05:33 +05:30