Merge remote-tracking branch 'origin/master' into raja_async_attributes

This commit is contained in:
boks1971
2026-06-13 17:49:58 +05:30
40 changed files with 1347 additions and 548 deletions
+1
View File
@@ -0,0 +1 @@
* @livekit/cs-devs
+1 -1
View File
@@ -71,7 +71,7 @@ jobs:
# Upload the original go test log as an artifact for later review.
- name: Upload test log
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
if: always()
with:
name: test-log
+2 -2
View File
@@ -62,14 +62,14 @@ jobs:
- name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
id: docker_build
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
+4 -1
View File
@@ -53,7 +53,10 @@ changelog:
- '^docs:'
- '^test:'
gomod:
proxy: true
# proxy must be disabled: it re-fetches modules from the Go proxy in a clean
# directory, which does not honor the replace directive in go.mod
# (replace github.com/pion/dtls/v3 => ... pinning v3.1.2 for gst whipsink compat)
proxy: false
mod: mod
checksum:
name_template: 'checksums.txt'
+38
View File
@@ -2,6 +2,44 @@
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.13.1] - 2026-06-08
### Fixed
- ci: disable goreleaser gomod proxy, incompatible with go.mod replace directive
## [1.13.0] - 2026-06-08
## ATTENTION: This release removes backwards compatibility for TURN authentication without TTL. Please refer to change log of [1.12.0](https://github.com/livekit/livekit/releases/tag/v1.12.0) for details on changes to TURN authentication and permissions handling. It was removed in https://github.com/livekit/livekit/pull/4539.
### Added
- rtc: report participant kind code and details (#4534)
- Add IsIntentionalDisconnect (#4537)
- rtc: emit per-data-track bytes via BytesTrackStats (#4540)
- Start tracking Twirp method request latency in prometheus too, not just in logs (#4545)
- Config documentation for advertise_internal_ip and skip_external_ip_validation (#4552, #4554, #4563)
- Metrics for participant active, i. e. fully established. (#4557)
- turn: allow for providing secret via file (#4564)
### Changed
- Always enable rtx codec (#4533)
- Don't require media sections when joining (#4535)
- Use NACKQuueInterface type. (#4538)
- Remove backwards compatibility support for TURN auth. (#4539)
- telemetry: split webhook-processed hook out of NewTelemetryService (#4548)
- rtc: prevent duration reporting for inactive participants (#4550)
- rtc: report participant session end time on room move (#4561)
- Update mediatransportutil to get ICE candidate timeout config (#4572)
### Fixed
- Check Less and LessEq in version compare. (#4532)
- fix sip error categorization (#4528)
- handle nil clientInfo (#4546, #4547)
- Prevent panic from nil(illegal) syncState.Subscriptions message (#4560)
## [1.12.0] - 2026-05-16
## ATTENTION: This release introduces important changes to how TURN authentication and permissions are handled. These changes make the system more secure. This release maintains backwards compatibility. However, backwards compatibility will be removed in the next release. So, please plan accordingly.
+4
View File
@@ -259,6 +259,10 @@ func startServer(ctx context.Context, c *cli.Command) error {
return err
}
if err = conf.LoadTURNSecrets(); err != nil {
return err
}
if cpuProfile := c.String("cpuprofile"); cpuProfile != "" {
if f, err := os.Create(cpuProfile); err != nil {
return err
+16 -2
View File
@@ -68,11 +68,11 @@ rtc:
# # when set to true, advertises both mapped external and internal IPs to clients as server candidates.
# # useful when clients connect from both private and public networks.
# # works only when `use_external_ip` is set to true.
# # when both this and `external_ip_only` are set, SFU advertises all private IPs with their mapped external IPs and skips
# # private IPs that do not have a mapped external IP.
# advertise_internal_ip: true
# # when set to true, SFU skips external IP accessibility checks via self-ping.
# # enable this when SFU is behind NAT that does not allow self-ping. only works when `use_external_ip` is set to true.
# # when both this and `external_ip_only` are set, SFU advertises all private IPs with their mapped external IPs and skips
# # private IPs that do not have a mapped external IP.
# skip_external_ip_validation: true
# # there are cases where the public IP determined via STUN is not the correct one
# # in such cases, use this setting to set the public IP of the node
@@ -98,6 +98,9 @@ rtc:
# protocol: tls
# # Shared secret for TURN server authentication
# secret: ""
# # Path for file containing shared secret for TURN server authentication
# # When both secret and secret_file are set, secret takes precedence
# secret_file: "/var/lib/coturn/secret"
# ttl: 14400 # seconds
# # Insecure username/password authentication
# username: ""
@@ -111,6 +114,12 @@ rtc:
# allow_pause: true
# # allows automatic connection fallback to TCP and TURN/TLS (if configured) when UDP has been unstable, default true
# allow_tcp_fallback: true
# # signaling RTT (in milliseconds) below which ICE/TCP is attempted on a UDP failure; at or above it,
# # supporting clients fall back directly to TURN/TLS. 0 (default) disables the check (ICE/TCP always tried).
# # a positive value also enables allow_udp_unstable_fallback.
# tcp_fallback_rtt_threshold: 0
# # migrate an established-but-lossy UDP connection to ICE/TCP or TURN/TLS. requires tcp_fallback_rtt_threshold > 0, default false
# allow_udp_unstable_fallback: false
# # number of packets to buffer in the SFU for video, defaults to 500
# packet_buffer_size_video: 500
# # number of packets to buffer in the SFU for audio, defaults to 200
@@ -161,6 +170,11 @@ rtc:
# when enabled, LiveKit will expose prometheus metrics on :6789/metrics
# prometheus_port: 6789
# expose /debug/pprof (and /debug/goroutine, /debug/rooms) on a dedicated port,
# separate from the public signalling port. only enabled when port is set.
# debug_handler_port:
# port: 7070
# API key / secret pairs.
# Keys are used for JWT authentication, server APIs would require a keypair in order to generate access tokens
# and make calls to the server
+74 -84
View File
@@ -8,52 +8,53 @@ require (
github.com/dennwc/iters v1.2.2
github.com/dustin/go-humanize v1.0.1
github.com/elliotchance/orderedmap/v3 v3.1.0
github.com/florianl/go-tc v0.4.7
github.com/florianl/go-tc v0.4.8
github.com/frostbyte73/core v0.1.1
github.com/gammazero/deque v1.2.1
github.com/gammazero/workerpool v1.2.1
github.com/google/uuid v1.6.0
github.com/google/wire v0.7.0
github.com/gorilla/websocket v1.5.3
github.com/hashicorp/go-version v1.8.0
github.com/hashicorp/go-version v1.9.0
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/jellydator/ttlcache/v3 v3.4.0
github.com/jxskiss/base62 v1.1.0
github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731
github.com/livekit/mediatransportutil v0.0.0-20260601042523-13d536af56d9
github.com/livekit/protocol v1.45.9-0.20260519061926-8381f2180c45
github.com/livekit/psrpc v0.7.1
github.com/livekit/mediatransportutil v0.0.0-20260608063931-a3417d38cda0
github.com/livekit/protocol v1.46.7-0.20260611165352-04a0fe5b5051
github.com/livekit/psrpc v0.7.2
github.com/mackerelio/go-osstat v0.2.7
github.com/magefile/mage v1.17.0
github.com/magefile/mage v1.17.2
github.com/maxbrunsfeld/counterfeiter/v6 v6.12.2
github.com/mitchellh/go-homedir v1.1.0
github.com/moby/moby/client v0.4.1
github.com/olekukonko/tablewriter v1.1.4
github.com/ory/dockertest/v3 v3.12.0
github.com/ory/dockertest/v4 v4.0.0
github.com/pion/datachannel v1.6.0
github.com/pion/dtls/v3 v3.1.2
github.com/pion/ice/v4 v4.2.6
github.com/pion/interceptor v0.1.44
github.com/pion/dtls/v3 v3.1.4
github.com/pion/ice/v4 v4.2.7
github.com/pion/interceptor v0.1.45
github.com/pion/rtcp v1.2.16
github.com/pion/rtp v1.10.1
github.com/pion/rtp v1.10.2
github.com/pion/sctp v1.9.5
github.com/pion/sdp/v3 v3.0.18
github.com/pion/transport/v4 v4.0.1
github.com/pion/turn/v5 v5.0.4
github.com/pion/transport/v4 v4.0.2
github.com/pion/turn/v5 v5.0.8
github.com/pion/webrtc/v4 v4.2.11
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.23.2
github.com/redis/go-redis/v9 v9.18.0
github.com/redis/go-redis/v9 v9.20.0
github.com/rs/cors v1.11.1
github.com/stretchr/testify v1.11.1
github.com/thoas/go-funk v0.9.3
github.com/tomnomnom/linkheader v0.0.0-20250811210735-e5fe3b51442e
github.com/twitchtv/twirp v8.1.3+incompatible
github.com/ua-parser/uap-go v0.0.0-20251207011819-db9adb27a0b8
github.com/ua-parser/uap-go v0.0.0-20260529044130-17c35e68e58c
github.com/urfave/negroni/v3 v3.1.1
go.uber.org/atomic v1.11.0
go.uber.org/multierr v1.11.0
go.uber.org/zap v1.27.1
golang.org/x/mod v0.34.0
go.uber.org/zap v1.28.0
golang.org/x/mod v0.36.0
golang.org/x/sync v0.20.0
google.golang.org/protobuf v1.36.11
gopkg.in/yaml.v3 v3.0.1
@@ -61,107 +62,96 @@ require (
require (
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/clipperhouse/displaywidth v0.10.0 // indirect
github.com/clipperhouse/uax29/v2 v2.6.0 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/cilium/ebpf v0.16.0 // indirect
github.com/clipperhouse/displaywidth v0.11.0 // indirect
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/fatih/color v1.19.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-jose/go-jose/v3 v3.0.5 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-viper/mapstructure/v2 v2.1.0 // indirect
github.com/goccy/go-json v0.10.6 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/moby/sys/user v0.3.0 // indirect
github.com/nyaruka/phonenumbers v1.6.5 // indirect
github.com/mattn/go-colorable v0.1.15 // indirect
github.com/mattn/go-isatty v0.0.22 // indirect
github.com/moby/moby/api v1.54.2 // indirect
github.com/nyaruka/phonenumbers v1.8.0 // indirect
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
github.com/olekukonko/errors v1.2.0 // indirect
github.com/olekukonko/ll v0.1.6 // indirect
github.com/olekukonko/errors v1.3.0 // indirect
github.com/olekukonko/ll v0.1.8 // indirect
github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel v1.43.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 // indirect
go.opentelemetry.io/otel/metric v1.43.0 // indirect
go.opentelemetry.io/otel/sdk v1.43.0 // indirect
go.opentelemetry.io/otel/trace v1.43.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 // indirect
go.opentelemetry.io/otel v1.44.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.44.0 // indirect
go.opentelemetry.io/otel/metric v1.44.0 // indirect
go.opentelemetry.io/otel/sdk v1.44.0 // indirect
go.opentelemetry.io/otel/trace v1.44.0 // indirect
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/exp v0.0.0-20260603202125-055de637280b // indirect
golang.org/x/time v0.15.0 // indirect
)
require (
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260209202127-80ab13bee0bf.1 // indirect
buf.build/go/protovalidate v1.1.3 // indirect
buf.build/go/protoyaml v0.6.0 // indirect
cel.dev/expr v0.25.1 // indirect
dario.cat/mergo v1.0.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260415201107-50325440f8f2.1 // indirect
buf.build/go/protovalidate v1.2.0 // indirect
buf.build/go/protoyaml v0.7.0 // indirect
cel.dev/expr v0.25.2 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/benbjohnson/clock v1.3.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/containerd/continuity v0.4.5 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/docker/cli v27.4.1+incompatible // indirect
github.com/docker/docker v27.1.1+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/docker/go-connections v0.7.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/go-jose/go-jose/v3 v3.0.5 // indirect
github.com/fsnotify/fsnotify v1.10.1 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/cel-go v0.27.0 // indirect
github.com/google/cel-go v0.28.1 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/subcommands v1.2.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/klauspost/compress v1.18.5 // indirect
github.com/klauspost/compress v1.18.6 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/lithammer/shortuuid/v4 v4.2.0 // indirect
github.com/mattn/go-runewidth v0.0.19 // indirect
github.com/mdlayher/netlink v1.7.1 // indirect
github.com/mdlayher/socket v0.4.0 // indirect
github.com/mattn/go-runewidth v0.0.24 // indirect
github.com/mdlayher/netlink v1.11.2 // indirect
github.com/mdlayher/socket v0.6.1 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nats-io/nats.go v1.50.0 // indirect
github.com/nats-io/nkeys v0.4.15 // indirect
github.com/nats-io/nats.go v1.52.0 // indirect
github.com/nats-io/nkeys v0.4.16 // indirect
github.com/nats-io/nuid v1.0.1 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/opencontainers/runc v1.2.3 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/pion/logging v0.2.4 // indirect
github.com/pion/mdns/v2 v2.1.0 // indirect
github.com/pion/randutil v0.1.0 // indirect
github.com/pion/srtp/v3 v3.0.10 // indirect
github.com/pion/stun/v3 v3.1.2
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pion/srtp/v3 v3.0.11 // indirect
github.com/pion/stun/v3 v3.1.4
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.66.1 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/urfave/cli/v3 v3.8.0
github.com/prometheus/common v0.68.1 // indirect
github.com/prometheus/procfs v0.20.1 // indirect
github.com/urfave/cli/v3 v3.9.0
github.com/wlynxg/anet v0.0.5 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/zeebo/xxh3 v1.1.0 // indirect
go.uber.org/zap/exp v0.3.0 // indirect
golang.org/x/crypto v0.50.0 // indirect
golang.org/x/net v0.53.0 // indirect
golang.org/x/sys v0.43.0 // indirect
golang.org/x/text v0.36.0 // indirect
golang.org/x/tools v0.43.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260427160629-7cedc36a6bc4 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260427160629-7cedc36a6bc4 // indirect
google.golang.org/grpc v1.80.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
golang.org/x/crypto v0.52.0 // indirect
golang.org/x/net v0.55.0 // indirect
golang.org/x/sys v0.45.0 // indirect
golang.org/x/text v0.37.0 // indirect
golang.org/x/tools v0.45.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
google.golang.org/grpc v1.81.1 // indirect
)
replace github.com/livekit/protocol => ../protocol
+155 -200
View File
@@ -1,21 +1,13 @@
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260209202127-80ab13bee0bf.1 h1:PMmTMyvHScV9Mn8wc6ASge9uRcHy0jtqPd+fM35LmsQ=
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260209202127-80ab13bee0bf.1/go.mod h1:tvtbpgaVXZX4g6Pn+AnzFycuRK3MOz5HJfEGeEllXYM=
buf.build/go/protovalidate v1.1.3 h1:m2GVEgQWd7rk+vIoAZ+f0ygGjvQTuqPQapBBdcpWVPE=
buf.build/go/protovalidate v1.1.3/go.mod h1:9XIuohWz+kj+9JVn3WQneHA5LZP50mjvneZMnbLkiIE=
buf.build/go/protoyaml v0.6.0 h1:Nzz1lvcXF8YgNZXk+voPPwdU8FjDPTUV4ndNTXN0n2w=
buf.build/go/protoyaml v0.6.0/go.mod h1:RgUOsBu/GYKLDSIRgQXniXbNgFlGEZnQpRAUdLAFV2Q=
cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=
cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260415201107-50325440f8f2.1 h1:s6hzCXtND/ICdGPTMGk7C+/BFlr2Jg5GyH0NKf4XGXg=
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260415201107-50325440f8f2.1/go.mod h1:tvtbpgaVXZX4g6Pn+AnzFycuRK3MOz5HJfEGeEllXYM=
buf.build/go/protovalidate v1.2.0 h1:DQVrUWkmGTBij+kOYv/x2LLxwcLaGKMdzShj1/6/3H0=
buf.build/go/protovalidate v1.2.0/go.mod h1:7rYiQEhqvAipoazpVNBBH2S2f8bjG4huMVy1V2Yofn4=
buf.build/go/protoyaml v0.7.0 h1:z4oVoFicbpPefhT7WAykxUdfp0yEQlhMQ2mCZOY5V38=
buf.build/go/protoyaml v0.7.0/go.mod h1:+a0cavd0uMvirb87xdu2ZMMmjlIQoiH/N2Ich5MGSQ0=
cel.dev/expr v0.25.2 h1:K6j46C81hXtZQfuX60cVWQFBJahKSE2gfRbNuvr5bFs=
cel.dev/expr v0.25.2/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
@@ -30,8 +22,6 @@ github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
@@ -41,46 +31,45 @@ github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2u
github.com/cilium/ebpf v0.8.1/go.mod h1:f5zLIM0FSNuAkSyLAN7X+Hy6yznlF1mNiWUMfxMtrgk=
github.com/cilium/ebpf v0.16.0 h1:+BiEnHL6Z7lXnlGUsXQPPAE7+kenAd4ES8MQ5min0Ok=
github.com/cilium/ebpf v0.16.0/go.mod h1:L7u2Blt2jMM/vLAVgjxluxtBKlz3/GWjB0dMOEngfwE=
github.com/clipperhouse/displaywidth v0.10.0 h1:GhBG8WuerxjFQQYeuZAeVTuyxuX+UraiZGD4HJQ3Y8g=
github.com/clipperhouse/displaywidth v0.10.0/go.mod h1:XqJajYsaiEwkxOj4bowCTMcT1SgvHo9flfF3jQasdbs=
github.com/clipperhouse/uax29/v2 v2.6.0 h1:z0cDbUV+aPASdFb2/ndFnS9ts/WNXgTNNGFoKXuhpos=
github.com/clipperhouse/uax29/v2 v2.6.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/d5/tengo/v2 v2.17.0 h1:BWUN9NoJzw48jZKiYDXDIF3QrIVZRm1uV1gTzeZ2lqM=
github.com/d5/tengo/v2 v2.17.0/go.mod h1:XRGjEs5I9jYIKTxly6HCF8oiiilk5E/RYXOZ5b0DZC8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dennwc/iters v1.2.2 h1:XH2/Etihiy9ZvPOVCR+icQXeYlhbvS7k0qro4x/2qQo=
github.com/dennwc/iters v1.2.2/go.mod h1:M9KuuMBeyEXYTmB7EnI9SCyALFCmPWOIxn5W1L0CjGg=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/docker/cli v27.4.1+incompatible h1:VzPiUlRJ/xh+otB75gva3r05isHMo5wXDfPRi5/b4hI=
github.com/docker/cli v27.4.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c=
github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/elliotchance/orderedmap/v3 v3.1.0 h1:j4DJ5ObEmMBt/lcwIecKcoRxIQUEnw0L804lXYDt/pg=
github.com/elliotchance/orderedmap/v3 v3.1.0/go.mod h1:G+Hc2RwaZvJMcS4JpGCOyViCnGeKf0bTYCGTO4uhjSo=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/florianl/go-tc v0.4.7 h1:Ysai5TIx4PgOzqI/1cse/pquOFCEkWofKtc/EPumfrg=
github.com/florianl/go-tc v0.4.7/go.mod h1:Fdz6eHitQZwylSvpAW3y9R9cUrnS/zinuAdjJpD7XqY=
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/florianl/go-tc v0.4.8 h1:hgmakUX1Nm0Ba1I0ZkbUl9CH6HbRwqSiwipnpmYp3Es=
github.com/florianl/go-tc v0.4.8/go.mod h1:B8GeOEnmrbOnxZtaCvsYJcgIzzmM8c/AIhtfCZsDj3Q=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
github.com/frostbyte73/core v0.1.1 h1:ChhJOR7bAKOCPbA+lqDLE2cGKlCG5JXsDvvQr4YaJIA=
github.com/frostbyte73/core v0.1.1/go.mod h1:mhfOtR+xWAvwXiwor7jnqPMnu4fxbv1F2MwZ0BEpzZo=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho=
github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo=
github.com/gammazero/deque v1.2.1 h1:9fnQVFCCZ9/NOc7ccTNqzoKd1tCWOqeI05/lPqFPMGQ=
github.com/gammazero/deque v1.2.1/go.mod h1:5nSFkzVm+afG9+gy0VIowlqVAW4N8zNcMne+CMQVD2g=
github.com/gammazero/workerpool v1.2.1 h1:MEDvUJsNYGuCvl1RwIXNKu2YtQtHqCSF9XWF04N7lqs=
@@ -92,16 +81,12 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-viper/mapstructure/v2 v2.1.0 h1:gHnMa2Y/pIxElCH2GlZZ1lZSsn6XMtufpGyP1XxdC/w=
github.com/go-viper/mapstructure/v2 v2.1.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/cel-go v0.27.0 h1:e7ih85+4qVrBuqQWTW4FKSqZYokVuc3HnhH5keboFTo=
github.com/google/cel-go v0.27.0/go.mod h1:tTJ11FWqnhw5KKpnWpvW9CJC3Y9GK4EIS0WXnBbebzw=
github.com/google/cel-go v0.28.1 h1:YWIwi77J4xIsYUwAF/iIuS6haffzIHS8yWI8glSbLWM=
github.com/google/cel-go v0.28.1/go.mod h1:X0bD6iVNR8pkROSOoHVdgTkzmRcosof7WQqCD6wcMc8=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@@ -114,8 +99,6 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE=
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
@@ -130,10 +113,10 @@ github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9n
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4=
github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48=
github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=
github.com/hashicorp/go-version v1.9.0 h1:CeOIz6k+LoN3qX9Z0tyQrPtiB1DFYRPfCIBtaXPSCnA=
github.com/hashicorp/go-version v1.9.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
@@ -156,10 +139,8 @@ github.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786 h1:N527AHMa79
github.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786/go.mod h1:v4hqbTdfQngbVSZJVWUhGE/lbTFf9jb+ygmNUDQMuOs=
github.com/jxskiss/base62 v1.1.0 h1:A5zbF8v8WXx2xixnAKD2w+abC+sIzYJX+nxmhA6HWFw=
github.com/jxskiss/base62 v1.1.0/go.mod h1:HhWAlUXvxKThfOlZbcuFzsqwtF5TcqS9ru3y5GfjWAc=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -173,26 +154,24 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lithammer/shortuuid/v4 v4.2.0 h1:LMFOzVB3996a7b8aBuEXxqOBflbfPQAiVzkIcHO0h8c=
github.com/lithammer/shortuuid/v4 v4.2.0/go.mod h1:D5noHZ2oFw/YaKCfGy0YxyE7M0wMbezmMjPdhyEFe6Y=
github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731 h1:9x+U2HGLrSw5ATTo469PQPkqzdoU7be46ryiCDO3boc=
github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731/go.mod h1:Rs3MhFwutWhGwmY1VQsygw28z5bWcnEYmS1OG9OxjOQ=
github.com/livekit/mediatransportutil v0.0.0-20260601042523-13d536af56d9 h1:kFXGyfVN9c2b66e6Q87evOvKrJE5u9e1Gd28gtjSLPI=
github.com/livekit/mediatransportutil v0.0.0-20260601042523-13d536af56d9/go.mod h1:KIBQV035+uwqfYvKPUh+7H3bXtaYO0TXhcxLosbhJ9A=
github.com/livekit/psrpc v0.7.1 h1:ms37az0QTD3UXIWuUC5D/SkmKOlRMVRsI261eBWu/Vw=
github.com/livekit/psrpc v0.7.1/go.mod h1:bZ4iHFQptTkbPnB0LasvRNu/OBYXEu1NA6O5BMFo9kk=
github.com/livekit/mediatransportutil v0.0.0-20260608063931-a3417d38cda0 h1:XHNNzebIKZRkLimla/hFGrAIX5EMWHctrgt3hLw7s+I=
github.com/livekit/mediatransportutil v0.0.0-20260608063931-a3417d38cda0/go.mod h1:o8CFmAdrVwzJNOCsQCLUzXRjokkufNshnQHOe4fRaqU=
github.com/livekit/psrpc v0.7.2 h1:6oZ+NODJ2pLyaT6VqDq1F4Qc/3TpDUSpyphj/P9MhQc=
github.com/livekit/psrpc v0.7.2/go.mod h1:rAI+m2+/cb4x9RXhLRtUx5ZwdfjjXOl4zi46IjEetaw=
github.com/mackerelio/go-osstat v0.2.7 h1:TCavZi10wF49bT6iQZ9eT2keGZQpC69MTDfdJej5e94=
github.com/mackerelio/go-osstat v0.2.7/go.mod h1:dwpYh5pIPmvk+IEwBKNIWRFMB92mrC08CmXOhDC7nQk=
github.com/magefile/mage v1.17.0 h1:dS4tkq997Ism03akafC8509iqDjeE7TNTexI25Y7sXM=
github.com/magefile/mage v1.17.0/go.mod h1:Yj51kqllmsgFpvvSzgrZPK9WtluG3kUhFaBUVLo4feA=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/magefile/mage v1.17.2 h1:fyXVu1eadI8Ap1HCCNgEhJ5McIWiYhLR8uol64ZZc40=
github.com/magefile/mage v1.17.2/go.mod h1:Yj51kqllmsgFpvvSzgrZPK9WtluG3kUhFaBUVLo4feA=
github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY=
github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4=
github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
github.com/mattn/go-runewidth v0.0.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/ay2DU=
github.com/mattn/go-runewidth v0.0.24/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/maxbrunsfeld/counterfeiter/v6 v6.12.2 h1:V23nK2R2B63g2GhygF9zVGpnigmhvoZoH8d0hrZwMGY=
github.com/maxbrunsfeld/counterfeiter/v6 v6.12.2/go.mod h1:Mr897yU9FmyKaQDPtRlVKibrjz40XXyOHUfyZBPSyZU=
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo=
@@ -207,57 +186,55 @@ github.com/mdlayher/netlink v1.2.2-0.20210123213345-5cc92139ae3e/go.mod h1:bacnN
github.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuriDdoPSWys=
github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8=
github.com/mdlayher/netlink v1.4.1/go.mod h1:e4/KuJ+s8UhfUpO9z00/fDZZmhSrs+oxyqAS9cNgn6Q=
github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA=
github.com/mdlayher/netlink v1.7.1 h1:FdUaT/e33HjEXagwELR8R3/KL1Fq5x3G5jgHLp/BTmg=
github.com/mdlayher/netlink v1.7.1/go.mod h1:nKO5CSjE/DJjVhk/TNp6vCE1ktVxEA8VEh8drhZzxsQ=
github.com/mdlayher/netlink v1.6.2/go.mod h1:O1HXX2sIWSMJ3Qn1BYZk1yZM+7iMki/uYGGiwGyq/iU=
github.com/mdlayher/netlink v1.11.2 h1:HKh2jqe+omdSWcQ88nrT7INE61B0NXfiSPFdgL4YbNI=
github.com/mdlayher/netlink v1.11.2/go.mod h1:uT2Yc/QLaZubzDpZIBi9d4GoeLwtp3x1AMeqSRrK2sA=
github.com/mdlayher/socket v0.0.0-20210307095302-262dc9984e00/go.mod h1:GAFlyu4/XV68LkQKYzKhIo/WW7j3Zi0YRAz/BOoanUc=
github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs=
github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw=
github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc=
github.com/mdlayher/socket v0.2.3/go.mod h1:bz12/FozYNH/VbvC3q7TRIK/Y6dH1kCKsXaUeXi/FmY=
github.com/mdlayher/socket v0.6.1 h1:M7uj2NtuujUY4mYr1C57NmfNiRHbkKpnBxO856lsc3A=
github.com/mdlayher/socket v0.6.1/go.mod h1:+/SGtqc9V+5dAuRgQsU0fGBI+oRDiW7O2Obx10OIWfg=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/moby/moby/api v1.54.2 h1:wiat9QAhnDQjA7wk1kh/TqHz2I1uUA7M7t9SAl/JNXg=
github.com/moby/moby/api v1.54.2/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs=
github.com/moby/moby/client v0.4.1 h1:DMQgisVoMkmMs7fp3ROSdiBnoAu8+vo3GggFl06M/wY=
github.com/moby/moby/client v0.4.1/go.mod h1:z52C9O2POPOsnxZAy//WtKcQ32P+jT/NGeXu/7nfjGQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/nats-io/nats.go v1.50.0 h1:5zAeQrTvyrKrWLJ0fu02W3br8ym57qf7csDzgLOpcds=
github.com/nats-io/nats.go v1.50.0/go.mod h1:26HypzazeOkyO3/mqd1zZd53STJN0EjCYF9Uy2ZOBno=
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
github.com/nats-io/nats.go v1.52.0 h1:n3avV4VBsCgsdwh71TppsTwtv+QdPs7ntSKM8qJLGsc=
github.com/nats-io/nats.go v1.52.0/go.mod h1:26HypzazeOkyO3/mqd1zZd53STJN0EjCYF9Uy2ZOBno=
github.com/nats-io/nkeys v0.4.16 h1:rd5oAuLOb8mnAycB0xleuEBNS1pVVnN0fv/FF34Eypg=
github.com/nats-io/nkeys v0.4.16/go.mod h1:llLgWoI0o4z/Q57q2R1kHfmocyhGV6VG/U18Glg1Afs=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/nyaruka/phonenumbers v1.6.5 h1:aBCaUhfpRA7hU6fsXk+p7KF1aNx4nQlq9hGeo2qdFg8=
github.com/nyaruka/phonenumbers v1.6.5/go.mod h1:7gjs+Lchqm49adhAKB5cdcng5ZXgt6x7Jgvi0ZorUtU=
github.com/nyaruka/phonenumbers v1.8.0 h1:TrXNJmbwcAHajzDqin3mLWw57vqLUA6ZjVdeNds0heQ=
github.com/nyaruka/phonenumbers v1.8.0/go.mod h1:fsKPJ70O9JetEA4ggnJadYTFWwtGPvu/lETTXNXq6Cs=
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc=
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
github.com/olekukonko/errors v1.2.0 h1:10Zcn4GeV59t/EGqJc8fUjtFT/FuUh5bTMzZ1XwmCRo=
github.com/olekukonko/errors v1.2.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
github.com/olekukonko/ll v0.1.6 h1:lGVTHO+Qc4Qm+fce/2h2m5y9LvqaW+DCN7xW9hsU3uA=
github.com/olekukonko/ll v0.1.6/go.mod h1:NVUmjBb/aCtUpjKk75BhWrOlARz3dqsM+OtszpY4o88=
github.com/olekukonko/errors v1.3.0 h1:teJvgLGUEqMzBUms+Dj3/3szNqCG/Jdw9iDbum8fR6U=
github.com/olekukonko/errors v1.3.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
github.com/olekukonko/ll v0.1.8 h1:ysHCJRGHYKzmBSdz9w5AySztx7lG8SQY+naTGYUbsz8=
github.com/olekukonko/ll v0.1.8/go.mod h1:RPRC6UcscfFZgjo1nulkfMH5IM0QAYim0LfnMvUuozw=
github.com/olekukonko/tablewriter v1.1.4 h1:ORUMI3dXbMnRlRggJX3+q7OzQFDdvgbN9nVWj1drm6I=
github.com/olekukonko/tablewriter v1.1.4/go.mod h1:+kedxuyTtgoZLwif3P1Em4hARJs+mVnzKxmsCL/C5RY=
github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28=
github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/opencontainers/runc v1.2.3 h1:fxE7amCzfZflJO2lHXf4y/y8M1BoAqp+FVmG19oYB80=
github.com/opencontainers/runc v1.2.3/go.mod h1:nSxcWUydXrsBZVYNSkTjoQ/N6rcyTtn+1SD5D4+kRIM=
github.com/ory/dockertest/v3 v3.12.0 h1:3oV9d0sDzlSQfHtIaB5k6ghUCVMVLpAY8hwrqoCyRCw=
github.com/ory/dockertest/v3 v3.12.0/go.mod h1:aKNDTva3cp8dwOWwb9cWuX84aH5akkxXRvO7KCwWVjE=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/ory/dockertest/v4 v4.0.0 h1:i19aFsO/VXE0VrMk4ifnKW4G/KIJ93PCjLOslxXoPME=
github.com/ory/dockertest/v4 v4.0.0/go.mod h1:b5Ofu8VIxWNhXFvQcLu17pRNQdoUBKtXBW74G4Ygzx8=
github.com/pion/datachannel v1.6.0 h1:XecBlj+cvsxhAMZWFfFcPyUaDZtd7IJvrXqlXD/53i0=
github.com/pion/datachannel v1.6.0/go.mod h1:ur+wzYF8mWdC+Mkis5Thosk+u/VOL287apDNEbFpsIk=
github.com/pion/dtls/v3 v3.1.2 h1:gqEdOUXLtCGW+afsBLO0LtDD8GnuBBjEy6HRtyofZTc=
github.com/pion/dtls/v3 v3.1.2/go.mod h1:Hw/igcX4pdY69z1Hgv5x7wJFrUkdgHwAn/Q/uo7YHRo=
github.com/pion/ice/v4 v4.2.6 h1:17SKPTupOmumCMAQ4ntf8rv6q6XEfVzuz1gSFrnhzPE=
github.com/pion/ice/v4 v4.2.6/go.mod h1:tmp90fBKpZhQDkHkp/QJb+Gn8vhMxuAcMq7PfhQWQHE=
github.com/pion/interceptor v0.1.44 h1:sNlZwM8dWXU9JQAkJh8xrarC0Etn8Oolcniukmuy0/I=
github.com/pion/interceptor v0.1.44/go.mod h1:4atVlBkcgXuUP+ykQF0qOCGU2j7pQzX2ofvPRFsY5RY=
github.com/pion/dtls/v3 v3.1.4 h1:QhvtMflMfu9Kf0RcDC5BJBle4caPskByrKQR6uuYqpY=
github.com/pion/dtls/v3 v3.1.4/go.mod h1:cr/qotLISUw/9C1m83ZPNZtj9WnXkYLpfCptPqbkInc=
github.com/pion/ice/v4 v4.2.7 h1:zDEbC6MiEdhQpF8TxBOTws+NU6ZgGpveHrQq4Lc1kao=
github.com/pion/ice/v4 v4.2.7/go.mod h1:9SNPaq0c7El/ki8leJzyCkK10zsskprR3zTNbO3monY=
github.com/pion/interceptor v0.1.45 h1:6PUo/5829bIfRFIPPJQzuDn8EjxRTSB/CSD7QVCOaqo=
github.com/pion/interceptor v0.1.45/go.mod h1:gNDYM/uFKcLe/B3gS2/7+aw6z+RDiMy2qKTnF1LO31w=
github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8=
github.com/pion/logging v0.2.4/go.mod h1:DffhXTKYdNZU+KtJ5pyQDjvOAh/GsNSyv1lbkFbe3so=
github.com/pion/mdns/v2 v2.1.0 h1:3IJ9+Xio6tWYjhN6WwuY142P/1jA0D5ERaIqawg/fOY=
@@ -266,57 +243,55 @@ github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
github.com/pion/rtcp v1.2.16 h1:fk1B1dNW4hsI78XUCljZJlC4kZOPk67mNRuQ0fcEkSo=
github.com/pion/rtcp v1.2.16/go.mod h1:/as7VKfYbs5NIb4h6muQ35kQF/J0ZVNz2Z3xKoCBYOo=
github.com/pion/rtp v1.10.1 h1:xP1prZcCTUuhO2c83XtxyOHJteISg6o8iPsE2acaMtA=
github.com/pion/rtp v1.10.1/go.mod h1:rF5nS1GqbR7H/TCpKwylzeq6yDM+MM6k+On5EgeThEM=
github.com/pion/rtp v1.10.2 h1:l+f6tTDcAH6xwepaAoW791ddhuYsJlqRATOzirO04Mo=
github.com/pion/rtp v1.10.2/go.mod h1:Au8fc6cEByy8RLTwKTQTEeQqDB/SJDxwL4mZuxYA5Pk=
github.com/pion/sctp v1.9.5 h1:QoSFB/drmAsmSeSFNQNI3xx010nW4HsycCZckRVWWag=
github.com/pion/sctp v1.9.5/go.mod h1:N20Dq6LY+JvJDAh9VVh1JELngb2rQ8dPgds5yBWiPgw=
github.com/pion/sdp/v3 v3.0.18 h1:l0bAXazKHpepazVdp+tPYnrsy9dfh7ZbT8DxesH5ZnI=
github.com/pion/sdp/v3 v3.0.18/go.mod h1:ZREGo6A9ZygQ9XkqAj5xYCQtQpif0i6Pa81HOiAdqQ8=
github.com/pion/srtp/v3 v3.0.10 h1:tFirkpBb3XccP5VEXLi50GqXhv5SKPxqrdlhDCJlZrQ=
github.com/pion/srtp/v3 v3.0.10/go.mod h1:3mOTIB0cq9qlbn59V4ozvv9ClW/BSEbRp4cY0VtaR7M=
github.com/pion/stun/v3 v3.1.2 h1:86IhD8wFn6IDW4b1/0QzoQS+f5PeA8OHHRn8UZW5ErY=
github.com/pion/stun/v3 v3.1.2/go.mod h1:H7gDic7nNwlUL05pbs6T1dtaBehh/KjupxfWw3ZI7cA=
github.com/pion/srtp/v3 v3.0.11 h1:GiESUr54/K4UuPigfq/CvWUed80JenQAHXn0C2MQQIQ=
github.com/pion/srtp/v3 v3.0.11/go.mod h1:EeZOi/sd6glM1EXapg051gdNWO9yWT1YSsgQ4SlJkns=
github.com/pion/stun/v3 v3.1.4 h1:/7ZL0j0dmLroKOq4GfkyKQ6asByYqntwyHSp5sYLcGY=
github.com/pion/stun/v3 v3.1.4/go.mod h1:ET7PFiXo1nrD2ZNVpbEHDuT0kCPVXhKmyWdiePNMw/U=
github.com/pion/transport/v3 v3.1.1 h1:Tr684+fnnKlhPceU+ICdrw6KKkTms+5qHMgw6bIkYOM=
github.com/pion/transport/v3 v3.1.1/go.mod h1:+c2eewC5WJQHiAA46fkMMzoYZSuGzA/7E2FPrOYHctQ=
github.com/pion/transport/v4 v4.0.1 h1:sdROELU6BZ63Ab7FrOLn13M6YdJLY20wldXW2Cu2k8o=
github.com/pion/transport/v4 v4.0.1/go.mod h1:nEuEA4AD5lPdcIegQDpVLgNoDGreqM/YqmEx3ovP4jM=
github.com/pion/transport/v4 v4.0.2 h1:ifYlPqNwsy6aKQ9y8yzxXlHae5431ZrH2avkD/Rn6Tk=
github.com/pion/transport/v4 v4.0.2/go.mod h1:06hFI+jCFcok2X2MekVufNZ/uzNZXivGBPfviSVcjgM=
github.com/pion/turn/v4 v4.1.4 h1:EU11yMXKIsK43FhcUnjLlrhE4nboHZq+TXBIi3QpcxQ=
github.com/pion/turn/v4 v4.1.4/go.mod h1:ES1DXVFKnOhuDkqn9hn5VJlSWmZPaRJLyBXoOeO/BmQ=
github.com/pion/turn/v5 v5.0.4 h1:xKAnP1b5eCnjFPd55OgxkqIVoyzbHKZa06SxZ3fopXQ=
github.com/pion/turn/v5 v5.0.4/go.mod h1:zbPsMp+fIVhKt5uWu2jcjk88FcoRbGMBSzVsaTuhcmM=
github.com/pion/turn/v5 v5.0.8 h1:pZUCtmwWCMkrRKqh/8pL3WoGADXBe0/lOPkN7oqFjK8=
github.com/pion/turn/v5 v5.0.8/go.mod h1:1VwvxElZaOdJU0liJ/WUSm/Tsh+n2OxS5ISSDxgOWxU=
github.com/pion/webrtc/v4 v4.2.11 h1:QUX1QZKlNIn4O7U5JxLPGP0sV5RTncZkzu9SPR3jVNU=
github.com/pion/webrtc/v4 v4.2.11/go.mod h1:s/rAiyy77GyRFrZMx+Ls6aua26dIBPudH8/ZHYbIRWY=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/prometheus/common v0.68.1 h1:omjRRl4QP4komogpXuhfeOiisQg7xdy8VM1UY+pStaY=
github.com/prometheus/common v0.68.1/go.mod h1:ZzL3f6u94qUxh9p+tJTrF+FvBS1XXbbRAZCQkytAL0Y=
github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc=
github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
github.com/redis/go-redis/v9 v9.18.0 h1:pMkxYPkEbMPwRdenAzUNyFNrDgHx9U+DrBabWNfSRQs=
github.com/redis/go-redis/v9 v9.18.0/go.mod h1:k3ufPphLU5YXwNTUcCRXGxUoF1fqxnhFQmscfkCoDA0=
github.com/redis/go-redis/v9 v9.20.0 h1:WnQYxLkgO2xiXTCJY0ldIiI8dNqCDlQAG+AtaH7a2a0=
github.com/redis/go-redis/v9 v9.20.0/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA=
github.com/rodaine/protogofakeit v0.1.1 h1:ZKouljuRM3A+TArppfBqnH8tGZHOwM/pjvtXe9DaXH8=
github.com/rodaine/protogofakeit v0.1.1/go.mod h1:pXn/AstBYMaSfc1/RqH3N82pBuxtWgejz1AlYpY1mI0=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rogpeppe/go-internal v1.15.0 h1:D0RCU5rMAp+SpgkiNdrjfJ+LX4J1M32V2NeCY7EJ6hc=
github.com/rogpeppe/go-internal v1.15.0/go.mod h1:DrUVZyrJU+txYW5/1kwtXQSMFio52ZOxX7yM1VHvnxs=
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM=
github.com/shoenig/test v1.7.0 h1:eWcHtTXa6QLnBvm0jgEabMRN/uJ4DMV3M8xUGgRkZmk=
github.com/shoenig/test v1.7.0/go.mod h1:UxJ6u/x2v/TNs/LoLxBNJRV9DiwBBKYxXSyczsBHFoI=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
@@ -327,23 +302,14 @@ github.com/tomnomnom/linkheader v0.0.0-20250811210735-e5fe3b51442e h1:tD38/4xg4n
github.com/tomnomnom/linkheader v0.0.0-20250811210735-e5fe3b51442e/go.mod h1:krvJ5AY/MjdPkTeRgMYbIDhbbbVvnPQPzsIsDJO8xrY=
github.com/twitchtv/twirp v8.1.3+incompatible h1:+F4TdErPgSUbMZMwp13Q/KgDVuI7HJXP61mNV3/7iuU=
github.com/twitchtv/twirp v8.1.3+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A=
github.com/ua-parser/uap-go v0.0.0-20251207011819-db9adb27a0b8 h1:yS0rzVnj7Z/ZeHzvv5erQbO2b8gyTL4CeMNodl9SJMQ=
github.com/ua-parser/uap-go v0.0.0-20251207011819-db9adb27a0b8/go.mod h1:gwANdYmo9R8LLwGnyDFWK2PMsaXXX2HhAvCnb/UhZsM=
github.com/urfave/cli/v3 v3.8.0 h1:XqKPrm0q4P0q5JpoclYoCAv0/MIvH/jZ2umzuf8pNTI=
github.com/urfave/cli/v3 v3.8.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
github.com/ua-parser/uap-go v0.0.0-20260529044130-17c35e68e58c h1:XbG4n3OWA1PcRTpbBA22E2ChPLvJCuwYRXO12tIyVL0=
github.com/ua-parser/uap-go v0.0.0-20260529044130-17c35e68e58c/go.mod h1:gwANdYmo9R8LLwGnyDFWK2PMsaXXX2HhAvCnb/UhZsM=
github.com/urfave/cli/v3 v3.9.0 h1:AV9lIiPv3ukYnxunaCUsHnEozptYmDN2F0+yWqLMn/c=
github.com/urfave/cli/v3 v3.9.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
github.com/urfave/negroni/v3 v3.1.1 h1:6MS4nG9Jk/UuCACaUlNXCbiKa0ywF9LXz5dGu09v8hw=
github.com/urfave/negroni/v3 v3.1.1/go.mod h1:jWvnX03kcSjDBl/ShB0iHvx5uOs7mAzZXW+JvJ5XYAs=
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
@@ -351,20 +317,22 @@ github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak=
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 h1:8tvICD4vSTOOsNrsI4Ljf6C+6UKvpTEH5XY3JMoyPoo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0/go.mod h1:z9+yiacE0IHRqM4qFfkbt/JYlmYXgss8GY/jXoNuPJI=
go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 h1:4YsVu3B8+3qtWYYrsUYgn0OG78pN0rnNPRGX4SbokQI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0/go.mod h1:+wnlSn0mD1ADVMe3v9Z/WIaiz6q6gL2J/ejaAmdmv80=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.44.0 h1:lgh3PiVrRUWMLOVSkQicxzZll5NjF1r+AtsX1XRIHw0=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.44.0/go.mod h1:5Cnhth3m/AgOeTgE3ex12pPmiu/gGtZit03kSzx9X7s=
go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58=
go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0=
go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI=
go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA=
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
@@ -373,56 +341,50 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo=
go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q=
go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U=
go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA=
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
golang.org/x/exp v0.0.0-20260603202125-055de637280b h1:v1uXiEBHo8QA0LiGCo7UgHMzHT4Kdfpl2zmtH5vaP1Q=
golang.org/x/exp v0.0.0-20260603202125-055de637280b/go.mod h1:d2fgXJLVs4dYDHUk5lwMIfzRzSrWCfGZb0ZqeLa/Vcw=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
@@ -446,20 +408,16 @@ golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -472,30 +430,27 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/genproto/googleapis/api v0.0.0-20260427160629-7cedc36a6bc4 h1:yOzSCGPx+cp5VO7IxvZ9SBFF7j1tZVcNtlHR2iYKtVo=
google.golang.org/genproto/googleapis/api v0.0.0-20260427160629-7cedc36a6bc4/go.mod h1:Q9HWtNeE7tM9npdIsEvqXj1QJIvVoeAV3rtXtS715Cw=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260427160629-7cedc36a6bc4 h1:tEkOQcXgF6dH1G+MVKZrfpYvozGrzb91k6ha7jireSM=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260427160629-7cedc36a6bc4/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8=
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ=
google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -504,10 +459,10 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=
pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
+49 -2
View File
@@ -49,8 +49,9 @@ const (
)
var (
ErrKeyFileIncorrectPermission = errors.New("key file others permissions must be set to 0")
ErrKeysNotSet = errors.New("one of key-file or keys must be provided")
ErrKeyFileIncorrectPermission = errors.New("key file others permissions must be set to 0")
ErrTURNSecretFileIncorrectPermission = errors.New("turn secret file others permissions must be set to 0")
ErrKeysNotSet = errors.New("one of key-file or keys must be provided")
)
type Config struct {
@@ -59,6 +60,7 @@ type Config struct {
// PrometheusPort is deprecated
PrometheusPort uint32 `yaml:"prometheus_port,omitempty"`
Prometheus PrometheusConfig `yaml:"prometheus,omitempty"`
DebugHandler DebugHandlerConfig `yaml:"debug_handler,omitempty"`
RTC RTCConfig `yaml:"rtc,omitempty"`
Redis redisLiveKit.RedisConfig `yaml:"redis,omitempty"`
Audio sfu.AudioConfig `yaml:"audio,omitempty"`
@@ -117,6 +119,18 @@ type RTCConfig struct {
// allow TCP and TURN/TLS fallback
AllowTCPFallback *bool `yaml:"allow_tcp_fallback,omitempty"`
// Signaling RTT threshold (in milliseconds) governing ICE/TCP fallback. On a UDP
// failure, ICE/TCP is attempted only while the measured signaling RTT is below this
// value; at or above it, supporting clients fall back directly to TURN/TLS. When 0
// (the default), the RTT check is disabled and ICE/TCP is always attempted (for
// clients that support it). A positive value also gates allow_udp_unstable_fallback.
TCPFallbackRTTThreshold int `yaml:"tcp_fallback_rtt_threshold,omitempty"`
// When enabled, an established UDP connection reporting sustained high packet loss is
// migrated to ICE/TCP or TURN/TLS. Requires tcp_fallback_rtt_threshold to be set
// (> 0). Disabled by default.
AllowUDPUnstableFallback bool `yaml:"allow_udp_unstable_fallback,omitempty"`
// force a reconnect on a publication error
ReconnectOnPublicationError *bool `yaml:"reconnect_on_publication_error,omitempty"`
@@ -151,6 +165,8 @@ type TURNServer struct {
// Secret is used for TURN static auth secrets mechanism. When provided,
// dynamic credentials are generated using HMAC-SHA1 instead of static Username/Credential
Secret string `yaml:"secret,omitempty"`
// File containing the secret
SecretFile string `yaml:"secret_file,omitempty"`
// TTL is the time-to-live in seconds for generated credentials when using Secret.
// Defaults to 14400 seconds (4 hours) if not specified
TTL int `yaml:"ttl,omitempty"`
@@ -370,6 +386,10 @@ type PrometheusConfig struct {
Password string `yaml:"password,omitempty"`
}
type DebugHandlerConfig struct {
Port uint32 `yaml:"port,omitempty"`
}
type ForwardStatsConfig struct {
SummaryInterval time.Duration `yaml:"summary_interval,omitempty"`
ReportInterval time.Duration `yaml:"report_interval,omitempty"`
@@ -681,6 +701,33 @@ func (conf *Config) ValidateKeys() error {
return nil
}
func (conf *Config) LoadTURNSecrets() error {
var otherFilter os.FileMode = 0o007
for i, s := range conf.RTC.TURNServers {
if s.SecretFile == "" {
continue
}
if s.Secret != "" {
logger.Warnw("both secret and secret_file are set for TURN server, the hardcoded secret will be used", nil,
"host", s.Host, "port", s.Port)
continue
}
st, err := os.Stat(s.SecretFile)
if err != nil {
return err
}
if st.Mode().Perm()&otherFilter != 0o000 {
return ErrTURNSecretFileIncorrectPermission
}
data, err := os.ReadFile(s.SecretFile)
if err != nil {
return fmt.Errorf("reading turn secret file %q: %w", s.SecretFile, err)
}
conf.RTC.TURNServers[i].Secret = strings.TrimSpace(string(data))
}
return nil
}
func GenerateCLIFlags(existingFlags []cli.Flag, hidden bool) ([]cli.Flag, error) {
defaultConfig := &DefaultConfig
flags := make([]cli.Flag, 0)
+16
View File
@@ -17,6 +17,7 @@ package routing
import (
"context"
"encoding/json"
"time"
"github.com/redis/go-redis/v9"
"go.uber.org/atomic"
@@ -192,6 +193,7 @@ type ParticipantInit struct {
AutoSubscribeDataTrack *bool
Client *livekit.ClientInfo
Grants *auth.ClaimGrants
TokenExpiresAt time.Time
Region string
AdaptiveStream bool
ID livekit.ParticipantID
@@ -224,6 +226,9 @@ func (pi *ParticipantInit) MarshalLogObject(e zapcore.ObjectEncoder) error {
logBoolPtr("AutoSubscribeDataTrack", pi.AutoSubscribeDataTrack)
e.AddObject("Client", logger.Proto(utils.ClientInfoWithoutAddress(pi.Client)))
e.AddObject("Grants", pi.Grants)
if !pi.TokenExpiresAt.IsZero() {
e.AddTime("TokenExpiresAt", pi.TokenExpiresAt)
}
e.AddString("Region", pi.Region)
logBoolPtr("AdaptiveStream", &pi.AdaptiveStream)
e.AddString("ID", string(pi.ID))
@@ -243,6 +248,11 @@ func (pi *ParticipantInit) ToStartSession(roomName livekit.RoomName, connectionI
return nil, err
}
var tokenExpiresAt int64
if !pi.TokenExpiresAt.IsZero() {
tokenExpiresAt = pi.TokenExpiresAt.Unix()
}
ss := &livekit.StartSession{
RoomName: string(roomName),
Identity: string(pi.Identity),
@@ -253,6 +263,7 @@ func (pi *ParticipantInit) ToStartSession(roomName livekit.RoomName, connectionI
AutoSubscribe: pi.AutoSubscribe,
Client: pi.Client,
GrantsJson: string(claims),
TokenExpiresAt: tokenExpiresAt,
AdaptiveStream: pi.AdaptiveStream,
ParticipantId: string(pi.ID),
DisableIceLite: pi.DisableICELite,
@@ -279,6 +290,10 @@ func ParticipantInitFromStartSession(ss *livekit.StartSession, region string) (*
if err := json.Unmarshal([]byte(ss.GrantsJson), claims); err != nil {
return nil, err
}
var tokenExpiresAt time.Time
if ss.TokenExpiresAt > 0 {
tokenExpiresAt = time.Unix(ss.TokenExpiresAt, 0)
}
pi := &ParticipantInit{
Identity: livekit.ParticipantIdentity(ss.Identity),
@@ -288,6 +303,7 @@ func ParticipantInitFromStartSession(ss *livekit.StartSession, region string) (*
Client: ss.Client,
AutoSubscribe: ss.AutoSubscribe,
Grants: claims,
TokenExpiresAt: tokenExpiresAt,
Region: region,
AdaptiveStream: ss.AdaptiveStream,
ID: livekit.ParticipantID(ss.ParticipantId),
+10 -1
View File
@@ -20,6 +20,7 @@ import (
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
"github.com/livekit/protocol/utils"
"github.com/livekit/livekit-server/pkg/config"
"github.com/livekit/livekit-server/pkg/telemetry/prometheus"
@@ -29,7 +30,7 @@ type NodeStats struct {
config config.NodeStatsConfig
startedAt int64
lock sync.Mutex
lock sync.RWMutex
statsHistory []*livekit.NodeStats
statsHistoryWritePtr int
}
@@ -80,3 +81,11 @@ func (n *NodeStats) UpdateAndGetNodeStats() (*livekit.NodeStats, error) {
n.statsHistoryWritePtr = (n.statsHistoryWritePtr + 1) % len(n.statsHistory)
return stats, nil
}
func (n *NodeStats) GetLatestNodeStats() *livekit.NodeStats {
n.lock.RLock()
defer n.lock.RUnlock()
latestPtr := (n.statsHistoryWritePtr - 1 + len(n.statsHistory)) % len(n.statsHistory)
return utils.CloneProto(n.statsHistory[latestPtr])
}
+28
View File
@@ -16,6 +16,7 @@ package rtc
import (
"fmt"
"sync"
"time"
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
@@ -40,6 +41,10 @@ type DataDownTrack struct {
params DataDownTrackParams
logger logger.Logger
createdAt int64
lock sync.Mutex
closed bool
onClose func()
}
func NewDataDownTrack(params DataDownTrackParams) (*DataDownTrack, error) {
@@ -59,11 +64,34 @@ func NewDataDownTrack(params DataDownTrackParams) (*DataDownTrack, error) {
}
func (d *DataDownTrack) Close() {
d.lock.Lock()
onClose := d.onClose
if d.closed {
d.lock.Unlock()
return
}
d.closed = true
d.lock.Unlock()
d.logger.Infow("closing data down track")
if d.params.BytesTrackStats != nil {
d.params.BytesTrackStats.Stop()
}
d.params.PublishDataTrack.DeleteDataDownTrack(d.SubscriberID())
if onClose != nil {
onClose()
}
}
func (d *DataDownTrack) OnClose(fn func()) {
d.lock.Lock()
d.onClose = fn
closed := d.closed
d.lock.Unlock()
if closed && fn != nil {
fn()
}
}
func (d *DataDownTrack) Handle() uint16 {
+43 -5
View File
@@ -16,9 +16,11 @@ package rtc
import (
"errors"
"slices"
"sync"
"github.com/frostbyte73/core"
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
"github.com/livekit/livekit-server/pkg/rtc/types"
sfuutils "github.com/livekit/livekit-server/pkg/sfu/utils"
@@ -40,6 +42,11 @@ type DataTrackParams struct {
BytesTrackStats *BytesTrackStats
}
type subscribedDataTrack struct {
subscriber types.LocalParticipant
dataDownTrack types.DataDownTrack
}
type DataTrack struct {
params DataTrackParams
@@ -47,7 +54,7 @@ type DataTrack struct {
lock sync.Mutex
dti *livekit.DataTrackInfo
subscribedTracks map[livekit.ParticipantID]types.DataDownTrack
subscribedTracks map[livekit.ParticipantID]subscribedDataTrack
downTrackSpreader *sfuutils.DownTrackSpreader[types.DataTrackSender]
@@ -60,7 +67,7 @@ func NewDataTrack(params DataTrackParams, dti *livekit.DataTrackInfo) *DataTrack
d := &DataTrack{
params: params,
dti: dti,
subscribedTracks: make(map[livekit.ParticipantID]types.DataDownTrack),
subscribedTracks: make(map[livekit.ParticipantID]subscribedDataTrack),
}
d.logger = params.Logger.WithValues("name", d.Name(), "handle", dti.PubHandle)
d.downTrackSpreader = sfuutils.NewDownTrackSpreader[types.DataTrackSender](sfuutils.DownTrackSpreaderParams{
@@ -136,18 +143,21 @@ func (d *DataTrack) AddSubscriber(sub types.LocalParticipant) (types.DataDownTra
return nil, err
}
d.subscribedTracks[sub.ID()] = dataDownTrack
d.subscribedTracks[sub.ID()] = subscribedDataTrack{
subscriber: sub,
dataDownTrack: dataDownTrack,
}
return dataDownTrack, nil
}
func (d *DataTrack) RemoveSubscriber(subID livekit.ParticipantID) {
d.lock.Lock()
dataDownTrack, ok := d.subscribedTracks[subID]
subscribedTrack, ok := d.subscribedTracks[subID]
delete(d.subscribedTracks, subID)
d.lock.Unlock()
if ok {
dataDownTrack.Close()
subscribedTrack.dataDownTrack.Close()
}
}
@@ -159,6 +169,34 @@ func (d *DataTrack) IsSubscriber(subID livekit.ParticipantID) bool {
return ok
}
func (d *DataTrack) RevokeDisallowedSubscribers(allowedSubscriberIdentities []livekit.ParticipantIdentity) []livekit.ParticipantIdentity {
var revokedSubscriberIdentities []livekit.ParticipantIdentity
d.lock.Lock()
disallowed := make(map[livekit.ParticipantID]livekit.ParticipantIdentity)
for subID, subscribedTrack := range d.subscribedTracks {
if IsParticipantExemptFromTrackPermissionsRestrictions(subscribedTrack.subscriber) {
continue
}
if !slices.Contains(allowedSubscriberIdentities, subscribedTrack.subscriber.Identity()) {
disallowed[subID] = subscribedTrack.subscriber.Identity()
}
}
d.lock.Unlock()
for subID, subIdentity := range disallowed {
d.logger.Infow("revoking data track subscription",
"subscriber", subIdentity,
"subscriberID", subID,
)
d.RemoveSubscriber(subID)
revokedSubscriberIdentities = append(revokedSubscriberIdentities, subIdentity)
}
return revokedSubscriberIdentities
}
func (d *DataTrack) AddDataDownTrack(dts types.DataTrackSender) error {
if d.closed.IsBroken() {
return errReceiverClosed
+71
View File
@@ -0,0 +1,71 @@
// Copyright 2023 LiveKit, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package rtc
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/livekit/livekit-server/pkg/rtc/types/typesfakes"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
"github.com/livekit/protocol/observability/roomobs"
)
func newTestDataTrackSubscriber(id livekit.ParticipantID, identity livekit.ParticipantIdentity, isRecorder bool) *typesfakes.FakeLocalParticipant {
sub := &typesfakes.FakeLocalParticipant{}
sub.IDReturns(id)
sub.IdentityReturns(identity)
sub.IsRecorderReturns(isRecorder)
sub.GetLoggerReturns(logger.GetLogger())
sub.GetReporterReturns(roomobs.NewNoopParticipantSessionReporter())
sub.GetTelemetryListenerReturns(&typesfakes.FakeParticipantTelemetryListener{})
return sub
}
func TestDataTrackRevokeDisallowedSubscribers(t *testing.T) {
dt := NewDataTrack(
DataTrackParams{
Logger: logger.GetLogger(),
ParticipantID: func() livekit.ParticipantID { return "pubID" },
ParticipantIdentity: "pub",
},
&livekit.DataTrackInfo{
PubHandle: 1,
Sid: "DTR_test",
Name: "test",
},
)
defer dt.Close()
allowed := newTestDataTrackSubscriber("allowedID", "allowed", false)
disallowed := newTestDataTrackSubscriber("disallowedID", "disallowed", false)
recorder := newTestDataTrackSubscriber("recorderID", "recorder", true)
for _, sub := range []*typesfakes.FakeLocalParticipant{allowed, disallowed, recorder} {
_, err := dt.AddSubscriber(sub)
require.NoError(t, err)
require.True(t, dt.IsSubscriber(sub.ID()))
}
revoked := dt.RevokeDisallowedSubscribers([]livekit.ParticipantIdentity{"allowed"})
require.Equal(t, []livekit.ParticipantIdentity{"disallowed"}, revoked)
// disallowed subscriber is removed, allowed and permission exempt (recorder) subscribers are kept
require.True(t, dt.IsSubscriber(allowed.ID()))
require.False(t, dt.IsSubscriber(disallowed.ID()))
require.True(t, dt.IsSubscriber(recorder.ID()))
}
+68 -58
View File
@@ -174,57 +174,58 @@ type ParticipantParams struct {
PLIThrottleConfig sfu.PLIThrottleConfig
CongestionControlConfig config.CongestionControlConfig
// codecs that are enabled for this room
PublishEnabledCodecs []*livekit.Codec
SubscribeEnabledCodecs []*livekit.Codec
Logger logger.Logger
LoggerResolver logger.DeferredFieldResolver
Reporter roomobs.ParticipantSessionReporter
ReporterResolver roomobs.ParticipantReporterResolver
SimTracks map[uint32]interceptor.SimulcastTrackInfo
Grants *auth.ClaimGrants
InitialVersion uint32
ClientConf *livekit.ClientConfiguration
ClientInfo ClientInfo
Region string
Migration bool
Reconnect bool
AdaptiveStream bool
AllowTCPFallback bool
TCPFallbackRTTThreshold int
AllowUDPUnstableFallback bool
TURNSEnabled bool
ParticipantListener types.LocalParticipantListener
ParticipantHelper types.LocalParticipantHelper
DisableSupervisor bool
ReconnectOnPublicationError bool
ReconnectOnSubscriptionError bool
ReconnectOnDataChannelError bool
VersionGenerator utils.TimedVersionGenerator
DisableDynacast bool
SubscriberAllowPause bool
SubscriptionLimitAudio int32
SubscriptionLimitVideo int32
PlayoutDelay *livekit.PlayoutDelay
SyncStreams bool
ForwardStats *sfu.ForwardStats
DisableSenderReportPassThrough bool
MetricConfig metric.MetricConfig
UseOneShotSignallingMode bool
EnableMetrics bool
DataChannelMaxBufferedAmount uint64
DatachannelSlowThreshold int
DatachannelLossyTargetLatency time.Duration
FireOnTrackBySdp bool
DisableCodecRegression bool
LastPubReliableSeq uint32
Country string
PreferVideoSizeFromMedia bool
UseSinglePeerConnection bool
EnableDataTracks bool
EnableRTPStreamRestartDetection bool
ForceBackupCodecPolicySimulcast bool
DisableTransceiverReuseForE2EE bool
EnableParticipantAsyncAttributes bool
PublishEnabledCodecs []*livekit.Codec
SubscribeEnabledCodecs []*livekit.Codec
Logger logger.Logger
LoggerResolver logger.DeferredFieldResolver
Reporter roomobs.ParticipantSessionReporter
ReporterResolver roomobs.ParticipantReporterResolver
SimTracks map[uint32]interceptor.SimulcastTrackInfo
Grants *auth.ClaimGrants
TokenExpiresAt time.Time
InitialVersion uint32
ClientConf *livekit.ClientConfiguration
ClientInfo ClientInfo
Region string
Migration bool
Reconnect bool
AdaptiveStream bool
AllowTCPFallback bool
TCPFallbackRTTThreshold int
AllowUDPUnstableFallback bool
TURNSEnabled bool
ParticipantListener types.LocalParticipantListener
ParticipantHelper types.LocalParticipantHelper
DisableSupervisor bool
ReconnectOnPublicationError bool
ReconnectOnSubscriptionError bool
ReconnectOnDataChannelError bool
VersionGenerator utils.TimedVersionGenerator
DisableDynacast bool
SubscriberAllowPause bool
SubscriptionLimitAudio int32
SubscriptionLimitVideo int32
PlayoutDelay *livekit.PlayoutDelay
SyncStreams bool
ForwardStats *sfu.ForwardStats
DisableSenderReportPassThrough bool
MetricConfig metric.MetricConfig
UseOneShotSignallingMode bool
EnableMetrics bool
DataChannelMaxBufferedAmount uint64
DatachannelSlowThreshold int
DatachannelLossyTargetLatency time.Duration
FireOnTrackBySdp bool
DisableCodecRegression bool
LastPubReliableSeq uint32
Country string
PreferVideoSizeFromMedia bool
UseSinglePeerConnection bool
EnableDataTracks bool
EnableRTPStreamRestartDetection bool
ForceBackupCodecPolicySimulcast bool
DisableTransceiverReuseForE2EE bool
EnableParticipantDataBlobs bool
}
type ParticipantImpl struct {
@@ -410,19 +411,23 @@ func NewParticipant(params ParticipantParams) (*ParticipantImpl, error) {
p.supervisor.OnPublicationError(p.onPublicationError)
}
var timerStarted bool
params.Reporter.RegisterFunc(func(ts time.Time, tx roomobs.ParticipantSessionTx) bool {
// Don't publish duration if participant never became active. Otherwise short-lived
// JOINING/JOINED -> DISCONNECTED transitions would still get rounded up to a
// minute by the session timer and inflate billed/reported duration.
if p.lastActiveAt.Load() == nil {
return !p.IsClosed()
}
if dts := p.disconnectedAt.Load(); dts != nil {
ts = *dts
tx.ReportEndTime(ts)
}
// Don't publish duration if participant never became active. Otherwise short-lived
// JOINING/JOINED -> DISCONNECTED transitions would still get rounded up to a
// minute by the session timer and inflate billed/reported duration.
if lastActive := p.lastActiveAt.Load(); lastActive == nil {
return !p.IsClosed()
} else if !timerStarted {
timerStarted = true
p.params.SessionTimer.Reset(*lastActive)
}
tx.ReportKindCode(roomobs.ParticipantKindCode(p.Kind()))
tx.ReportKindDetailsCodes(roomobs.ParticipantKindDetailsCodes(p.KindDetails()))
@@ -787,6 +792,10 @@ func (p *ParticipantImpl) ClaimGrants() *auth.ClaimGrants {
return p.grants.Load()
}
func (p *ParticipantImpl) TokenExpiresAt() time.Time {
return p.params.TokenExpiresAt
}
func (p *ParticipantImpl) SetPermission(permission *livekit.ParticipantPermission) bool {
if permission == nil {
return false
@@ -4075,6 +4084,7 @@ func (p *ParticipantImpl) MoveToRoom(params types.MoveToRoomParams) {
p.telemetryGuard = &telemetry.ReferenceGuard{}
p.lock.Unlock()
p.params.Reporter.ReportEndTime(time.Now())
p.params.LoggerResolver.Reset()
p.params.ReporterResolver.Reset()
p.setListener(params.Listener)
+28
View File
@@ -16,6 +16,7 @@ package rtc
import (
"github.com/livekit/livekit-server/pkg/rtc/datatrack"
"github.com/livekit/livekit-server/pkg/rtc/types"
"github.com/livekit/protocol/livekit"
"github.com/livekit/protocol/logger"
"github.com/livekit/protocol/utils"
@@ -156,6 +157,33 @@ func (p *ParticipantImpl) onReceivedDataTrackMessage(data []byte, arrivalTime in
p.listener().OnDataTrackMessage(p, data, &packet)
}
// wraps the promoted UpTrackManager.UpdateSubscriptionPermission to also revoke
// data track subscriptions that are no longer permitted
func (p *ParticipantImpl) UpdateSubscriptionPermission(
subscriptionPermission *livekit.SubscriptionPermission,
timedVersion utils.TimedVersion,
resolverBySid func(participantID livekit.ParticipantID) types.LocalParticipant,
) error {
if err := p.UpTrackManager.UpdateSubscriptionPermission(subscriptionPermission, timedVersion, resolverBySid); err != nil {
return err
}
p.maybeRevokeDataTrackSubscriptions()
return nil
}
func (p *ParticipantImpl) maybeRevokeDataTrackSubscriptions() {
for _, dt := range p.UpDataTrackManager.GetPublishedDataTracks() {
allowed := p.UpTrackManager.GetAllowedSubscribers(dt.ID())
if allowed == nil {
// no restrictions
continue
}
dt.RevokeDisallowedSubscribers(allowed)
}
}
func (p *ParticipantImpl) GetNextSubscribedDataTrackHandle() uint16 {
p.lock.Lock()
defer p.lock.Unlock()
+14 -4
View File
@@ -686,14 +686,14 @@ func (r *Room) onSyncState(participant types.LocalParticipant, state *livekit.Sy
// synthesize a track setting for each disabled track,
// can be set before adding subscriptions,
// in fact it is done before so that setting can be updated immediately upon subscription.
for _, trackSid := range state.TrackSidsDisabled {
for _, trackSid := range state.GetTrackSidsDisabled() {
participant.UpdateSubscribedTrackSettings(livekit.TrackID(trackSid), &livekit.UpdateTrackSettings{Disabled: true})
}
participant.HandleUpdateSubscriptions(
livekit.StringsAsIDs[livekit.TrackID](state.Subscription.TrackSids),
state.Subscription.ParticipantTracks,
state.Subscription.Subscribe,
livekit.StringsAsIDs[livekit.TrackID](state.GetSubscription().GetTrackSids()),
state.GetSubscription().GetParticipantTracks(),
state.GetSubscription().GetSubscribe(),
)
return nil
}
@@ -705,6 +705,9 @@ func (r *Room) onUpdateSubscriptionPermission(participant types.LocalParticipant
for _, track := range participant.GetPublishedTracks() {
r.trackManager.NotifyTrackChanged(track.ID())
}
for _, track := range participant.GetPublishedDataTracks() {
r.trackManager.NotifyTrackChanged(track.ID())
}
return nil
}
@@ -746,6 +749,13 @@ func (r *Room) ResolveDataTrackForSubscriber(sub types.LocalParticipant, trackID
res.TrackRemovedNotifier = r.trackManager.GetOrCreateTrackRemoveNotifier(trackID)
res.PublisherIdentity = info.PublisherIdentity
res.PublisherID = info.PublisherID
pub := r.GetParticipantByID(info.PublisherID)
// when publisher is not found, we will assume it doesn't have permission to access
if pub != nil {
res.HasPermission = IsParticipantExemptFromTrackPermissionsRestrictions(sub) || pub.HasPermission(trackID, sub.Identity())
}
return res
}
+72 -42
View File
@@ -612,8 +612,9 @@ func (m *SubscriptionManager) reconcileDataTrackSubscription(s *dataTrackSubscri
s.recordAttempt(false)
switch err {
case ErrNoSubscribePermission:
case ErrNoTrackPermission, ErrNoSubscribePermission:
// these are errors that are outside of our control, so we'll keep trying
// - ErrNoTrackPermission: publisher did not grant subscriber permission, may change any moment
// - ErrNoSubscribePermission: participant was not granted canSubscribe, may change any moment
case ErrTrackNotFound:
// source track was never published or closed
@@ -1083,6 +1084,14 @@ func (m *SubscriptionManager) subscribeDataTrack(sub *dataTrackSubscription) err
sub.setPublisher(res.PublisherIdentity, res.PublisherID)
permChanged := sub.setHasPermission(res.HasPermission)
if permChanged {
m.params.Participant.SendSubscriptionPermissionUpdate(sub.getPublisherID(), trackID, res.HasPermission)
}
if !res.HasPermission {
return ErrNoTrackPermission
}
dataDownTrack, err := dataTrack.AddSubscriber(m.params.Participant)
if err != nil && !errors.Is(err, errAlreadySubscribed) {
return err
@@ -1091,6 +1100,9 @@ func (m *SubscriptionManager) subscribeDataTrack(sub *dataTrackSubscription) err
sub.logger.Debugw("already subscribed to data track")
}
if err == nil && dataDownTrack != nil { // subTrack could be nil if already subscribed
dataDownTrack.OnClose(func() {
m.handleDataDownTrackClose(sub)
})
sub.setDataDownTrack(dataDownTrack)
sub.logger.Debugw("subscribed to data track")
}
@@ -1111,11 +1123,27 @@ func (m *SubscriptionManager) unsubscribeDataTrack(s *dataTrackSubscription) err
dataTrack := dataDownTrack.PublishDataTrack()
dataTrack.RemoveSubscriber(s.subscriberID)
return nil
}
// DataDownTrack closing is how the publisher signifies that the subscription is no longer fulfilled
// this could be due to a few reasons:
// - subscriber-initiated unsubscribe
// - data track was unpublished
// - publisher revoked permissions for the participant
func (m *SubscriptionManager) handleDataDownTrackClose(s *dataTrackSubscription) {
s.logger.Debugw("data down track closed")
if s.getDataDownTrack() == nil {
return
}
s.setDataDownTrack(nil)
s.setChangedNotifier(nil)
s.setRemovedNotifier(nil)
m.unmarkSubscribedTo(s.getPublisherID(), s.trackID)
return nil
m.notifyDataTrackSubscriberHandles()
m.queueReconcileDataTrack(s.trackID)
}
func (m *SubscriptionManager) notifyDataTrackSubscriberHandles() {
@@ -1208,12 +1236,14 @@ type trackSubscription struct {
trackID livekit.TrackID
logger logger.Logger
lock sync.RWMutex
desired bool
publisherID livekit.ParticipantID
publisherIdentity livekit.ParticipantIdentity
changedNotifier types.ChangeNotifier
removedNotifier types.ChangeNotifier
lock sync.RWMutex
desired bool
publisherID livekit.ParticipantID
publisherIdentity livekit.ParticipantIdentity
changedNotifier types.ChangeNotifier
removedNotifier types.ChangeNotifier
hasPermissionInitialized bool
hasPermission bool
numAttempts atomic.Int32
@@ -1225,6 +1255,31 @@ type trackSubscription struct {
subscribeAt atomic.Pointer[time.Time]
}
// set permission and return true if it has changed
func (s *trackSubscription) setHasPermission(perm bool) bool {
s.lock.Lock()
defer s.lock.Unlock()
if s.hasPermissionInitialized && s.hasPermission == perm {
return false
}
s.hasPermissionInitialized = true
s.hasPermission = perm
if s.hasPermission {
// when permission is granted, reset the timer so it has sufficient time to reconcile
t := time.Now()
s.subStartedAt.Store(&t)
s.subscribeAt.Store(&t)
}
return true
}
func (s *trackSubscription) getHasPermission() bool {
s.lock.RLock()
defer s.lock.RUnlock()
return s.hasPermission
}
func (s *trackSubscription) setPublisher(publisherIdentity livekit.ParticipantIdentity, publisherID livekit.ParticipantID) {
s.lock.Lock()
defer s.lock.Unlock()
@@ -1355,13 +1410,11 @@ func (s *trackSubscription) handleSourceTrackRemoved() {
type mediaTrackSubscription struct {
trackSubscription
settings *livekit.UpdateTrackSettings
hasPermissionInitialized bool
hasPermission bool
subscribedTrack types.SubscribedTrack
eventSent atomic.Bool
bound bool
kind atomic.Pointer[livekit.TrackType]
settings *livekit.UpdateTrackSettings
subscribedTrack types.SubscribedTrack
eventSent atomic.Bool
bound bool
kind atomic.Pointer[livekit.TrackType]
succRecordCounter atomic.Int32
}
@@ -1379,31 +1432,6 @@ func newMediaTrackSubscription(subscriberID livekit.ParticipantID, trackID livek
return s
}
// set permission and return true if it has changed
func (s *mediaTrackSubscription) setHasPermission(perm bool) bool {
s.lock.Lock()
defer s.lock.Unlock()
if s.hasPermissionInitialized && s.hasPermission == perm {
return false
}
s.hasPermissionInitialized = true
s.hasPermission = perm
if s.hasPermission {
// when permission is granted, reset the timer so it has sufficient time to reconcile
t := time.Now()
s.subStartedAt.Store(&t)
s.subscribeAt.Store(&t)
}
return true
}
func (s *mediaTrackSubscription) getHasPermission() bool {
s.lock.RLock()
defer s.lock.RUnlock()
return s.hasPermission
}
func (s *mediaTrackSubscription) setSubscribedTrack(track types.SubscribedTrack) {
s.lock.Lock()
oldTrack := s.subscribedTrack
@@ -1573,6 +1601,7 @@ func (s *dataTrackSubscription) needsCleanup() bool {
func (s *dataTrackSubscription) setDataDownTrack(dataDownTrack types.DataDownTrack) {
s.lock.Lock()
oldDataDownTrack := s.dataDownTrack
s.dataDownTrack = dataDownTrack
subscriptionOptions := s.subscriptionOptions
s.lock.Unlock()
@@ -1581,8 +1610,9 @@ func (s *dataTrackSubscription) setDataDownTrack(dataDownTrack types.DataDownTra
s.logger.Debugw("restoring data track subscription options", "subscriptionOptions", logger.Proto(subscriptionOptions))
dataDownTrack.UpdateSubscriptionOptions(subscriptionOptions)
}
// DT-TODO - DataTrack close callback on previous if not nil?, see setSubscribedTrack for example
if oldDataDownTrack != nil {
oldDataDownTrack.OnClose(nil)
}
}
func (s *dataTrackSubscription) getDataDownTrack() types.DataDownTrack {
+138 -2
View File
@@ -204,9 +204,9 @@ func TestUnsubscribe(t *testing.T) {
publisherID: "pubID",
publisherIdentity: "pub",
logger: logger.GetLogger(),
hasPermission: true,
},
hasPermission: true,
bound: true,
bound: true,
}
// a bunch of unfortunate manual wiring
res := resolver.Resolve(nil, s.trackID)
@@ -445,6 +445,84 @@ func TestSubscriptionLimits(t *testing.T) {
require.Len(t, sm.GetSubscribedTracks(), 1)
}
func TestSubscribeDataTrack(t *testing.T) {
t.Run("no track permission", func(t *testing.T) {
sm := newTestSubscriptionManager()
defer sm.Close(false)
resolver := newTestDataTrackResolver(false, true, "pub", "pubID")
sm.params.DataTrackResolver = resolver.Resolve
sm.SubscribeToDataTrack("track")
sm.lock.RLock()
s := sm.dataTrackSubscriptions["track"]
sm.lock.RUnlock()
require.Eventually(t, func() bool {
return !s.getHasPermission()
}, subSettleTimeout, subCheckInterval, "should not have permission to subscribe")
time.Sleep(subscriptionTimeout)
// isDesired remains unchanged, no subscriber added to the data track
require.True(t, s.isDesired())
require.True(t, s.needsSubscribe())
require.Equal(t, 0, resolver.dataTrack.AddSubscriberCallCount())
// subscriber should have been notified that permission was denied
p := sm.params.Participant.(*typesfakes.FakeLocalParticipant)
require.Equal(t, 1, p.SendSubscriptionPermissionUpdateCallCount())
_, _, allowed := p.SendSubscriptionPermissionUpdateArgsForCall(0)
require.False(t, allowed)
// give permissions now
resolver.SetHasPermission(true)
require.Eventually(t, func() bool {
return !s.needsSubscribe()
}, subSettleTimeout, subCheckInterval, "should be subscribed")
require.NotNil(t, s.getDataDownTrack())
require.Equal(t, 1, resolver.dataTrack.AddSubscriberCallCount())
})
t.Run("permission revoked while subscribed", func(t *testing.T) {
sm := newTestSubscriptionManager()
defer sm.Close(false)
resolver := newTestDataTrackResolver(true, true, "pub", "pubID")
sm.params.DataTrackResolver = resolver.Resolve
sm.SubscribeToDataTrack("track")
sm.lock.RLock()
s := sm.dataTrackSubscriptions["track"]
sm.lock.RUnlock()
require.Eventually(t, func() bool {
return !s.needsSubscribe()
}, subSettleTimeout, subCheckInterval, "should be subscribed")
// revoke permission and close the down track like the publisher would
// (DataTrack.RevokeDisallowedSubscribers -> RemoveSubscriber -> DataDownTrack.Close)
resolver.SetHasPermission(false)
ddt := s.getDataDownTrack()
require.NotNil(t, ddt)
setTestDataDownTrackClosed(t, ddt)
// subscription stays desired but is denied on resubscribe attempts
require.Nil(t, s.getDataDownTrack())
require.True(t, s.isDesired())
require.Eventually(t, func() bool {
return !s.getHasPermission()
}, subSettleTimeout, subCheckInterval, "should not have permission to resubscribe")
require.True(t, s.needsSubscribe())
require.Equal(t, 1, resolver.dataTrack.AddSubscriberCallCount())
// give permission back, should resubscribe
resolver.SetHasPermission(true)
require.Eventually(t, func() bool {
return !s.needsSubscribe() && s.getDataDownTrack() != nil
}, subSettleTimeout, subCheckInterval, "should be resubscribed")
require.Equal(t, 2, resolver.dataTrack.AddSubscriberCallCount())
})
}
type testSubscriptionParams struct {
SubscriptionLimitAudio int32
SubscriptionLimitVideo int32
@@ -526,6 +604,57 @@ func (t *testResolver) Resolve(_subscriber types.LocalParticipant, trackID livek
return res
}
type testDataTrackResolver struct {
lock sync.Mutex
hasPermission bool
hasTrack bool
pubIdentity livekit.ParticipantIdentity
pubID livekit.ParticipantID
dataTrack *typesfakes.FakeDataTrack
}
func newTestDataTrackResolver(hasPermission bool, hasTrack bool, pubIdentity livekit.ParticipantIdentity, pubID livekit.ParticipantID) *testDataTrackResolver {
r := &testDataTrackResolver{
hasPermission: hasPermission,
hasTrack: hasTrack,
pubIdentity: pubIdentity,
pubID: pubID,
dataTrack: &typesfakes.FakeDataTrack{},
}
r.dataTrack.PublisherIDReturns(pubID)
r.dataTrack.PublisherIdentityReturns(pubIdentity)
r.dataTrack.AddSubscriberCalls(func(sub types.LocalParticipant) (types.DataDownTrack, error) {
ddt := &typesfakes.FakeDataDownTrack{}
ddt.PublishDataTrackReturns(r.dataTrack)
return ddt, nil
})
return r
}
func (t *testDataTrackResolver) SetHasPermission(hasPermission bool) {
t.lock.Lock()
defer t.lock.Unlock()
t.hasPermission = hasPermission
}
func (t *testDataTrackResolver) Resolve(_subscriber types.LocalParticipant, trackID livekit.TrackID) types.DataResolverResult {
t.lock.Lock()
defer t.lock.Unlock()
res := types.DataResolverResult{
TrackChangedNotifier: utils.NewChangeNotifier(),
TrackRemovedNotifier: utils.NewChangeNotifier(),
HasPermission: t.hasPermission,
PublisherID: t.pubID,
PublisherIdentity: t.pubIdentity,
}
if t.hasTrack {
t.dataTrack.IDReturns(trackID)
res.DataTrack = t.dataTrack
}
return res
}
func setTestSubscribedTrackBound(t *testing.T, st types.SubscribedTrack) {
fst, ok := st.(*typesfakes.FakeSubscribedTrack)
require.True(t, ok)
@@ -541,3 +670,10 @@ func setTestSubscribedTrackClosed(t *testing.T, st types.SubscribedTrack, isExpe
fst.OnCloseArgsForCall(0)(isExpectedToResume)
}
func setTestDataDownTrackClosed(t *testing.T, ddt types.DataDownTrack) {
fddt, ok := ddt.(*typesfakes.FakeDataDownTrack)
require.True(t, ok)
fddt.OnCloseArgsForCall(0)()
}
+26 -1
View File
@@ -232,6 +232,8 @@ type PCTransport struct {
resetShortConnOnICERestart atomic.Bool
signalingRTT atomic.Uint32 // milliseconds
hasFullyEstablishedRecorded bool
debouncedNegotiate *sfuutils.Debouncer
debouncePending bool
lastNegotiate time.Time
@@ -717,6 +719,8 @@ func (t *PCTransport) setICEConnectedAt(at time.Time) {
t.tcpICETimer.Stop()
t.tcpICETimer = nil
}
prometheus.RecordPeerConnectionState(t.params.Transport, "ice_connected")
}
if t.mayFailedICEStatsTimer != nil {
@@ -801,6 +805,7 @@ func (t *PCTransport) setConnectedAt(at time.Time) bool {
t.firstConnectedAt = at
prometheus.RecordServiceOperationSuccess("peer_connection")
prometheus.RecordPeerConnectionState(t.params.Transport, "connected")
t.lock.Unlock()
return true
}
@@ -964,6 +969,13 @@ func (t *PCTransport) onDataChannel(dc *webrtc.DataChannel) {
func (t *PCTransport) maybeNotifyFullyEstablished() {
if t.isFullyEstablished() {
t.params.Handler.OnFullyEstablished()
t.lock.Lock()
if !t.hasFullyEstablishedRecorded {
t.hasFullyEstablishedRecorded = true
prometheus.RecordPeerConnectionState(t.params.Transport, "fully_established")
}
t.lock.Unlock()
}
}
@@ -2604,6 +2616,8 @@ func (t *PCTransport) createAndSendOffer(options *webrtc.OfferOptions) error {
t.params.Logger.Debugw("local offer (unfiltered)", "sdp", offer.SDP)
}
isStartOfConnectionSequence := t.pc.LocalDescription() == nil
err = t.pc.SetLocalDescription(offer)
if err != nil {
if errors.Is(err, webrtc.ErrConnectionClosed) {
@@ -2615,6 +2629,10 @@ func (t *PCTransport) createAndSendOffer(options *webrtc.OfferOptions) error {
return errors.Wrap(err, "setting local description failed")
}
if isStartOfConnectionSequence {
prometheus.RecordPeerConnectionState(t.params.Transport, "started")
}
//
// Filter after setting local description as pion expects the offer
// to match between CreateOffer and SetLocalDescription.
@@ -2788,7 +2806,7 @@ func (t *PCTransport) createAndSendAnswer() error {
return errors.Wrap(err, "could not send answer")
}
t.localAnswerId.Store(answerId)
prometheus.RecordServiceOperationSuccess("asnwer")
prometheus.RecordServiceOperationSuccess("answer")
if err := t.sendUnmatchedMediaRequirement(false); err != nil {
return err
@@ -2860,9 +2878,16 @@ func (t *PCTransport) handleRemoteOfferReceived(sd *webrtc.SessionDescription, o
t.outputAndClearICEStats()
}
isStartOfConnectionSequence := t.pc.RemoteDescription() == nil
if err := t.setRemoteDescription(*sd); err != nil {
return err
}
if isStartOfConnectionSequence {
prometheus.RecordPeerConnectionState(t.params.Transport, "started")
}
t.params.Handler.OnSetRemoteDescriptionOffer()
t.processSendersPendingConfig()
+7 -2
View File
@@ -444,6 +444,7 @@ type LocalParticipant interface {
// permissions
ClaimGrants() *auth.ClaimGrants
TokenExpiresAt() time.Time
SetPermission(permission *livekit.ParticipantPermission) bool
CanPublish() bool
CanPublishSource(source livekit.TrackSource) bool
@@ -839,6 +840,7 @@ type DataTrack interface {
AddSubscriber(sub LocalParticipant) (DataDownTrack, error)
RemoveSubscriber(participantID livekit.ParticipantID)
IsSubscriber(subID livekit.ParticipantID) bool
RevokeDisallowedSubscribers(allowedSubscriberIdentities []livekit.ParticipantIdentity) []livekit.ParticipantIdentity
AddDataDownTrack(sender DataTrackSender) error
DeleteDataDownTrack(subscriberID livekit.ParticipantID)
@@ -851,6 +853,7 @@ type DataTrack interface {
//counterfeiter:generate . DataDownTrack
type DataDownTrack interface {
Close()
OnClose(fn func())
Handle() uint16
PublishDataTrack() DataTrack
@@ -915,8 +918,10 @@ type DataResolverResult struct {
TrackChangedNotifier ChangeNotifier
TrackRemovedNotifier ChangeNotifier
DataTrack DataTrack
PublisherID livekit.ParticipantID
PublisherIdentity livekit.ParticipantIdentity
// is permission given to the requesting participant
HasPermission bool
PublisherID livekit.ParticipantID
PublisherIdentity livekit.ParticipantIdentity
}
// MediaTrackResolver locates a specific media track for a subscriber
@@ -23,6 +23,11 @@ type FakeDataDownTrack struct {
handleReturnsOnCall map[int]struct {
result1 uint16
}
OnCloseStub func(func())
onCloseMutex sync.RWMutex
onCloseArgsForCall []struct {
arg1 func()
}
PublishDataTrackStub func() types.DataTrack
publishDataTrackMutex sync.RWMutex
publishDataTrackArgsForCall []struct {
@@ -119,6 +124,38 @@ func (fake *FakeDataDownTrack) HandleReturnsOnCall(i int, result1 uint16) {
}{result1}
}
func (fake *FakeDataDownTrack) OnClose(arg1 func()) {
fake.onCloseMutex.Lock()
fake.onCloseArgsForCall = append(fake.onCloseArgsForCall, struct {
arg1 func()
}{arg1})
stub := fake.OnCloseStub
fake.recordInvocation("OnClose", []interface{}{arg1})
fake.onCloseMutex.Unlock()
if stub != nil {
fake.OnCloseStub(arg1)
}
}
func (fake *FakeDataDownTrack) OnCloseCallCount() int {
fake.onCloseMutex.RLock()
defer fake.onCloseMutex.RUnlock()
return len(fake.onCloseArgsForCall)
}
func (fake *FakeDataDownTrack) OnCloseCalls(stub func(func())) {
fake.onCloseMutex.Lock()
defer fake.onCloseMutex.Unlock()
fake.OnCloseStub = stub
}
func (fake *FakeDataDownTrack) OnCloseArgsForCall(i int) func() {
fake.onCloseMutex.RLock()
defer fake.onCloseMutex.RUnlock()
argsForCall := fake.onCloseArgsForCall[i]
return argsForCall.arg1
}
func (fake *FakeDataDownTrack) PublishDataTrack() types.DataTrack {
fake.publishDataTrackMutex.Lock()
ret, specificReturn := fake.publishDataTrackReturnsOnCall[len(fake.publishDataTrackArgsForCall)]
@@ -116,6 +116,17 @@ type FakeDataTrack struct {
removeSubscriberArgsForCall []struct {
arg1 livekit.ParticipantID
}
RevokeDisallowedSubscribersStub func([]livekit.ParticipantIdentity) []livekit.ParticipantIdentity
revokeDisallowedSubscribersMutex sync.RWMutex
revokeDisallowedSubscribersArgsForCall []struct {
arg1 []livekit.ParticipantIdentity
}
revokeDisallowedSubscribersReturns struct {
result1 []livekit.ParticipantIdentity
}
revokeDisallowedSubscribersReturnsOnCall map[int]struct {
result1 []livekit.ParticipantIdentity
}
ToProtoStub func() *livekit.DataTrackInfo
toProtoMutex sync.RWMutex
toProtoArgsForCall []struct {
@@ -708,6 +719,72 @@ func (fake *FakeDataTrack) RemoveSubscriberArgsForCall(i int) livekit.Participan
return argsForCall.arg1
}
func (fake *FakeDataTrack) RevokeDisallowedSubscribers(arg1 []livekit.ParticipantIdentity) []livekit.ParticipantIdentity {
var arg1Copy []livekit.ParticipantIdentity
if arg1 != nil {
arg1Copy = make([]livekit.ParticipantIdentity, len(arg1))
copy(arg1Copy, arg1)
}
fake.revokeDisallowedSubscribersMutex.Lock()
ret, specificReturn := fake.revokeDisallowedSubscribersReturnsOnCall[len(fake.revokeDisallowedSubscribersArgsForCall)]
fake.revokeDisallowedSubscribersArgsForCall = append(fake.revokeDisallowedSubscribersArgsForCall, struct {
arg1 []livekit.ParticipantIdentity
}{arg1Copy})
stub := fake.RevokeDisallowedSubscribersStub
fakeReturns := fake.revokeDisallowedSubscribersReturns
fake.recordInvocation("RevokeDisallowedSubscribers", []interface{}{arg1Copy})
fake.revokeDisallowedSubscribersMutex.Unlock()
if stub != nil {
return stub(arg1)
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeDataTrack) RevokeDisallowedSubscribersCallCount() int {
fake.revokeDisallowedSubscribersMutex.RLock()
defer fake.revokeDisallowedSubscribersMutex.RUnlock()
return len(fake.revokeDisallowedSubscribersArgsForCall)
}
func (fake *FakeDataTrack) RevokeDisallowedSubscribersCalls(stub func([]livekit.ParticipantIdentity) []livekit.ParticipantIdentity) {
fake.revokeDisallowedSubscribersMutex.Lock()
defer fake.revokeDisallowedSubscribersMutex.Unlock()
fake.RevokeDisallowedSubscribersStub = stub
}
func (fake *FakeDataTrack) RevokeDisallowedSubscribersArgsForCall(i int) []livekit.ParticipantIdentity {
fake.revokeDisallowedSubscribersMutex.RLock()
defer fake.revokeDisallowedSubscribersMutex.RUnlock()
argsForCall := fake.revokeDisallowedSubscribersArgsForCall[i]
return argsForCall.arg1
}
func (fake *FakeDataTrack) RevokeDisallowedSubscribersReturns(result1 []livekit.ParticipantIdentity) {
fake.revokeDisallowedSubscribersMutex.Lock()
defer fake.revokeDisallowedSubscribersMutex.Unlock()
fake.RevokeDisallowedSubscribersStub = nil
fake.revokeDisallowedSubscribersReturns = struct {
result1 []livekit.ParticipantIdentity
}{result1}
}
func (fake *FakeDataTrack) RevokeDisallowedSubscribersReturnsOnCall(i int, result1 []livekit.ParticipantIdentity) {
fake.revokeDisallowedSubscribersMutex.Lock()
defer fake.revokeDisallowedSubscribersMutex.Unlock()
fake.RevokeDisallowedSubscribersStub = nil
if fake.revokeDisallowedSubscribersReturnsOnCall == nil {
fake.revokeDisallowedSubscribersReturnsOnCall = make(map[int]struct {
result1 []livekit.ParticipantIdentity
})
}
fake.revokeDisallowedSubscribersReturnsOnCall[i] = struct {
result1 []livekit.ParticipantIdentity
}{result1}
}
func (fake *FakeDataTrack) ToProto() *livekit.DataTrackInfo {
fake.toProtoMutex.Lock()
ret, specificReturn := fake.toProtoReturnsOnCall[len(fake.toProtoArgsForCall)]
@@ -1384,6 +1384,16 @@ type FakeLocalParticipant struct {
result1 *livekit.ParticipantInfo
result2 utils.TimedVersion
}
TokenExpiresAtStub func() time.Time
tokenExpiresAtMutex sync.RWMutex
tokenExpiresAtArgsForCall []struct {
}
tokenExpiresAtReturns struct {
result1 time.Time
}
tokenExpiresAtReturnsOnCall map[int]struct {
result1 time.Time
}
UncacheDownTrackStub func(*webrtc.RTPTransceiver)
uncacheDownTrackMutex sync.RWMutex
uncacheDownTrackArgsForCall []struct {
@@ -8901,6 +8911,59 @@ func (fake *FakeLocalParticipant) ToProtoWithVersionReturnsOnCall(i int, result1
}{result1, result2}
}
func (fake *FakeLocalParticipant) TokenExpiresAt() time.Time {
fake.tokenExpiresAtMutex.Lock()
ret, specificReturn := fake.tokenExpiresAtReturnsOnCall[len(fake.tokenExpiresAtArgsForCall)]
fake.tokenExpiresAtArgsForCall = append(fake.tokenExpiresAtArgsForCall, struct {
}{})
stub := fake.TokenExpiresAtStub
fakeReturns := fake.tokenExpiresAtReturns
fake.recordInvocation("TokenExpiresAt", []interface{}{})
fake.tokenExpiresAtMutex.Unlock()
if stub != nil {
return stub()
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeLocalParticipant) TokenExpiresAtCallCount() int {
fake.tokenExpiresAtMutex.RLock()
defer fake.tokenExpiresAtMutex.RUnlock()
return len(fake.tokenExpiresAtArgsForCall)
}
func (fake *FakeLocalParticipant) TokenExpiresAtCalls(stub func() time.Time) {
fake.tokenExpiresAtMutex.Lock()
defer fake.tokenExpiresAtMutex.Unlock()
fake.TokenExpiresAtStub = stub
}
func (fake *FakeLocalParticipant) TokenExpiresAtReturns(result1 time.Time) {
fake.tokenExpiresAtMutex.Lock()
defer fake.tokenExpiresAtMutex.Unlock()
fake.TokenExpiresAtStub = nil
fake.tokenExpiresAtReturns = struct {
result1 time.Time
}{result1}
}
func (fake *FakeLocalParticipant) TokenExpiresAtReturnsOnCall(i int, result1 time.Time) {
fake.tokenExpiresAtMutex.Lock()
defer fake.tokenExpiresAtMutex.Unlock()
fake.TokenExpiresAtStub = nil
if fake.tokenExpiresAtReturnsOnCall == nil {
fake.tokenExpiresAtReturnsOnCall = make(map[int]struct {
result1 time.Time
})
}
fake.tokenExpiresAtReturnsOnCall[i] = struct {
result1 time.Time
}{result1}
}
func (fake *FakeLocalParticipant) UncacheDownTrack(arg1 *webrtc.RTPTransceiver) {
fake.uncacheDownTrackMutex.Lock()
fake.uncacheDownTrackArgsForCall = append(fake.uncacheDownTrackArgsForCall, struct {
+8 -1
View File
@@ -49,7 +49,7 @@ type UpTrackManager struct {
// publishedTracks that participant is publishing
publishedTracks map[livekit.TrackID]types.MediaTrack
subscriptionPermission *livekit.SubscriptionPermission
// subscriber permission for published tracks
// subscriber permission for published tracks (both media and data)
subscriberPermissions map[livekit.ParticipantIdentity]*livekit.TrackPermission // subscriberIdentity => *livekit.TrackPermission
lock sync.RWMutex
@@ -235,6 +235,13 @@ func (u *UpTrackManager) HasPermission(trackID livekit.TrackID, subIdentity live
return u.hasPermissionLocked(trackID, subIdentity)
}
func (u *UpTrackManager) GetAllowedSubscribers(trackID livekit.TrackID) []livekit.ParticipantIdentity {
u.lock.RLock()
defer u.lock.RUnlock()
return u.getAllowedSubscribersLocked(trackID)
}
func (u *UpTrackManager) UpdatePublishedAudioTrack(update *livekit.UpdateLocalAudioTrack) types.MediaTrack {
track := u.GetPublishedTrack(livekit.TrackID(update.TrackSid))
if track != nil {
+29 -7
View File
@@ -19,6 +19,7 @@ import (
"errors"
"net/http"
"strings"
"time"
"github.com/twitchtv/twirp"
@@ -35,8 +36,9 @@ const (
type grantsKey struct{}
type grantsValue struct {
claims *auth.ClaimGrants
apiKey string
claims *auth.ClaimGrants
apiKey string
expiresAt time.Time
}
var (
@@ -90,17 +92,23 @@ func (m *APIKeyAuthMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request,
return
}
_, grants, err := v.Verify(secret)
claims, grants, err := v.Verify(secret)
if err != nil {
HandleError(w, r, http.StatusUnauthorized, errors.New("invalid token: "+authToken+", error: "+err.Error()))
return
}
var expiresAt time.Time
if claims != nil && claims.ExpiresAt != nil {
expiresAt = claims.ExpiresAt.Time
}
// set grants in context
ctx := r.Context()
r = r.WithContext(context.WithValue(ctx, grantsKey{}, &grantsValue{
claims: grants,
apiKey: v.APIKey(),
claims: grants,
apiKey: v.APIKey(),
expiresAt: expiresAt,
}))
}
@@ -123,6 +131,15 @@ func GetGrants(ctx context.Context) *auth.ClaimGrants {
return v.claims
}
func GetTokenExpiresAt(ctx context.Context) time.Time {
val := ctx.Value(grantsKey{})
v, ok := val.(*grantsValue)
if !ok {
return time.Time{}
}
return v.expiresAt
}
func GetAPIKey(ctx context.Context) string {
val := ctx.Value(grantsKey{})
v, ok := val.(*grantsValue)
@@ -133,9 +150,14 @@ func GetAPIKey(ctx context.Context) string {
}
func WithGrants(ctx context.Context, grants *auth.ClaimGrants, apiKey string) context.Context {
return WithGrantsExpiry(ctx, grants, apiKey, time.Time{})
}
func WithGrantsExpiry(ctx context.Context, grants *auth.ClaimGrants, apiKey string, expiresAt time.Time) context.Context {
return context.WithValue(ctx, grantsKey{}, &grantsValue{
claims: grants,
apiKey: apiKey,
claims: grants,
apiKey: apiKey,
expiresAt: expiresAt,
})
}
+17 -10
View File
@@ -15,27 +15,31 @@
package service_test
import (
"context"
"fmt"
"log"
"net"
"os"
"testing"
"time"
"go.uber.org/atomic"
"github.com/ory/dockertest/v3"
mobyclient "github.com/moby/moby/client"
"github.com/ory/dockertest/v4"
)
var Docker *dockertest.Pool
var Docker dockertest.ClosablePool
func TestMain(m *testing.M) {
pool, err := dockertest.NewPool("")
ctx := context.Background()
pool, err := dockertest.NewPool(ctx, "")
if err != nil {
log.Fatalf("Could not construct pool: %s", err)
}
// uses pool to try to connect to Docker
err = pool.Client.Ping()
_, err = pool.Client().Ping(ctx, mobyclient.PingOptions{})
if err != nil {
log.Fatalf("Could not connect to Docker: %s", err)
}
@@ -46,7 +50,7 @@ func TestMain(m *testing.M) {
}
func waitTCPPort(t testing.TB, addr string) {
if err := Docker.Retry(func() error {
if err := Docker.Retry(t.Context(), 30*time.Second, func() error {
conn, err := net.Dial("tcp", addr)
if err != nil {
t.Log(err)
@@ -62,15 +66,18 @@ func waitTCPPort(t testing.TB, addr string) {
var redisLast atomic.Uint32
func runRedis(t testing.TB) string {
c, err := Docker.RunWithOptions(&dockertest.RunOptions{
Name: fmt.Sprintf("lktest-redis-%d", redisLast.Inc()),
Repository: "redis", Tag: "latest",
})
c, err := Docker.Run(t.Context(),
"redis",
dockertest.WithName(fmt.Sprintf("lktest-redis-%d", redisLast.Inc())),
dockertest.WithTag("latest"),
)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
_ = Docker.Purge(c)
// t.Context() is canceled before cleanup funcs run, so use a
// non-canceled context to let the container stop/remove complete.
_ = c.Close(context.Background())
})
addr := c.GetHostPort("6379/tcp")
waitTCPPort(t, addr)
+36 -7
View File
@@ -72,6 +72,33 @@ func NewEgressLauncher(client rpc.EgressClient, io IOClient, store ServiceStore)
}
}
func (s *EgressService) StartEgress(ctx context.Context, req *livekit.StartEgressRequest) (*livekit.EgressInfo, error) {
sourceType, outputType := egress.GetTypes(&livekit.EgressInfo_Egress{Egress: req})
fields := []any{
"room", req.RoomName,
"sourceType", sourceType,
"outputType", outputType,
}
defer func() {
AppendLogFields(ctx, fields...)
}()
egressID, idFromCtx := EgressID(ctx)
ei, err := s.startEgress(ctx, &rpc.StartEgressRequest{
EgressId: egressID,
Request: &rpc.StartEgressRequest_Egress{
Egress: req,
},
})
if err != nil {
return nil, err
}
if !idFromCtx {
fields = append(fields, "egressID", ei.EgressId)
}
return ei, err
}
func (s *EgressService) StartRoomCompositeEgress(ctx context.Context, req *livekit.RoomCompositeEgressRequest) (*livekit.EgressInfo, error) {
fields := []any{
"room", req.RoomName,
@@ -230,6 +257,8 @@ func (s *egressLauncher) StartEgress(ctx context.Context, req *rpc.StartEgressRe
roomName = v.TrackComposite.RoomName
case *rpc.StartEgressRequest_Track:
roomName = v.Track.RoomName
case *rpc.StartEgressRequest_Egress:
roomName = v.Egress.RoomName
}
if roomName != "" {
@@ -254,13 +283,6 @@ func (s *egressLauncher) StartEgress(ctx context.Context, req *rpc.StartEgressRe
return info, nil
}
func (s *egressLauncher) StopEgress(ctx context.Context, req *livekit.StopEgressRequest) (*livekit.EgressInfo, error) {
if s.client == nil {
return nil, ErrEgressNotConnected
}
return s.client.StopEgress(ctx, req.EgressId, req)
}
type LayoutMetadata struct {
Layout string `json:"layout"`
}
@@ -371,3 +393,10 @@ func (s *EgressService) StopEgress(ctx context.Context, req *livekit.StopEgressR
return info, nil
}
func (s *egressLauncher) StopEgress(ctx context.Context, req *livekit.StopEgressRequest) (*livekit.EgressInfo, error) {
if s.client == nil {
return nil, ErrEgressNotConnected
}
return s.client.StopEgress(ctx, req.EgressId, req)
}
+41 -29
View File
@@ -471,34 +471,37 @@ func (r *RoomManager) StartSession(
}
participant, err = rtc.NewParticipant(rtc.ParticipantParams{
Identity: pi.Identity,
Name: pi.Name,
SID: sid,
Config: &rtcConf,
Sink: responseSink,
AudioConfig: r.config.Audio,
VideoConfig: r.config.Video,
LimitConfig: r.config.Limit,
ProtocolVersion: pv,
SessionStartTime: sessionStartTime,
SessionTimer: observability.NewSessionTimer(sessionStartTime),
TelemetryListener: room.ParticipantTelemetryListener(),
Trailer: room.Trailer(),
PLIThrottleConfig: r.config.RTC.PLIThrottle,
CongestionControlConfig: r.config.RTC.CongestionControl,
PublishEnabledCodecs: enabledCodecs,
SubscribeEnabledCodecs: enabledCodecs,
Grants: pi.Grants,
Reconnect: pi.Reconnect,
Logger: pLogger,
Reporter: roomobs.NewNoopParticipantSessionReporter(),
ClientConf: clientConf,
ClientInfo: rtc.ClientInfo{ClientInfo: pi.Client},
Region: pi.Region,
AdaptiveStream: pi.AdaptiveStream,
AllowTCPFallback: allowFallback,
TURNSEnabled: r.config.IsTURNSEnabled(),
ParticipantListener: room.LocalParticipantListener(),
Identity: pi.Identity,
Name: pi.Name,
SID: sid,
Config: &rtcConf,
Sink: responseSink,
AudioConfig: r.config.Audio,
VideoConfig: r.config.Video,
LimitConfig: r.config.Limit,
ProtocolVersion: pv,
SessionStartTime: sessionStartTime,
SessionTimer: observability.NewSessionTimer(sessionStartTime),
TelemetryListener: room.ParticipantTelemetryListener(),
Trailer: room.Trailer(),
PLIThrottleConfig: r.config.RTC.PLIThrottle,
CongestionControlConfig: r.config.RTC.CongestionControl,
PublishEnabledCodecs: enabledCodecs,
SubscribeEnabledCodecs: enabledCodecs,
Grants: pi.Grants,
TokenExpiresAt: pi.TokenExpiresAt,
Reconnect: pi.Reconnect,
Logger: pLogger,
Reporter: roomobs.NewNoopParticipantSessionReporter(),
ClientConf: clientConf,
ClientInfo: rtc.ClientInfo{ClientInfo: pi.Client},
Region: pi.Region,
AdaptiveStream: pi.AdaptiveStream,
AllowTCPFallback: allowFallback,
TCPFallbackRTTThreshold: r.config.RTC.TCPFallbackRTTThreshold,
AllowUDPUnstableFallback: r.config.RTC.AllowUDPUnstableFallback,
TURNSEnabled: r.config.IsTURNSEnabled(),
ParticipantListener: room.LocalParticipantListener(),
ParticipantHelper: &roomManagerParticipantHelper{
room: room,
codecRegressionThreshold: r.config.Video.CodecRegressionThreshold,
@@ -1123,11 +1126,20 @@ func (r *RoomManager) refreshToken(participant types.LocalParticipant) error {
}
grants := participant.ClaimGrants()
// Preserve the original token's expiry
validFor := tokenDefaultTTL
if expiresAt := participant.TokenExpiresAt(); !expiresAt.IsZero() {
if remaining := time.Until(expiresAt); remaining > validFor {
validFor = remaining
}
}
token := auth.NewAccessToken(key, secret)
token.SetName(grants.Name).
SetIdentity(string(participant.Identity())).
SetKind(grants.GetParticipantKind()).
SetValidFor(tokenDefaultTTL).
SetValidFor(validFor).
SetMetadata(grants.Metadata).
SetAttributes(grants.Attributes).
SetVideoGrant(grants.Video).
+2
View File
@@ -218,6 +218,7 @@ func (s *RTCService) validateInternal(
Identity: livekit.ParticipantIdentity(res.grants.Identity),
Name: livekit.ParticipantName(res.grants.Name),
Grants: res.grants,
TokenExpiresAt: res.tokenExpiresAt,
Region: res.region,
CreateRoom: res.createRoomRequest,
UseSinglePeerConnection: useSinglePeerConnection,
@@ -364,6 +365,7 @@ func (s *RTCService) serve(w http.ResponseWriter, r *http.Request, needsJoinRequ
roomName, pi, code, err = s.validateInternal(pLogger, r, needsJoinRequest, false)
if err != nil {
prometheus.IncrementParticipantJoinValidationFail(1)
resolveLogger(true)
HandleError(w, r, code, err, getLoggerFields()...)
return
+35 -1
View File
@@ -21,7 +21,7 @@ import (
"fmt"
"net"
"net/http"
_ "net/http/pprof"
httppprof "net/http/pprof"
"runtime"
"runtime/pprof"
"strconv"
@@ -53,6 +53,7 @@ type LivekitServer struct {
agentService *AgentService
httpServer *http.Server
promServer *http.Server
debugServer *http.Server
router routing.Router
roomManager *RoomManager
signalServer *SignalServer
@@ -171,6 +172,20 @@ func NewLivekitServer(conf *config.Config,
}
}
if conf.DebugHandler.Port > 0 {
debugMux := http.NewServeMux()
debugMux.HandleFunc("/debug/pprof/", httppprof.Index)
debugMux.HandleFunc("/debug/pprof/cmdline", httppprof.Cmdline)
debugMux.HandleFunc("/debug/pprof/profile", httppprof.Profile)
debugMux.HandleFunc("/debug/pprof/symbol", httppprof.Symbol)
debugMux.HandleFunc("/debug/pprof/trace", httppprof.Trace)
debugMux.HandleFunc("/debug/goroutine", s.debugGoroutines)
debugMux.HandleFunc("/debug/rooms", s.debugInfo)
s.debugServer = &http.Server{
Handler: http.Handler(debugMux),
}
}
if err = router.RemoveDeadNodes(); err != nil {
return
}
@@ -221,6 +236,7 @@ func (s *LivekitServer) Start() error {
// ensure we could listen
listeners := make([]net.Listener, 0)
promListeners := make([]net.Listener, 0)
debugListeners := make([]net.Listener, 0)
for _, addr := range addresses {
ln, err := net.Listen("tcp", net.JoinHostPort(addr, strconv.Itoa(int(s.config.Port))))
if err != nil {
@@ -235,6 +251,14 @@ func (s *LivekitServer) Start() error {
}
promListeners = append(promListeners, ln)
}
if s.debugServer != nil {
ln, err = net.Listen("tcp", net.JoinHostPort(addr, strconv.Itoa(int(s.config.DebugHandler.Port))))
if err != nil {
return err
}
debugListeners = append(debugListeners, ln)
}
}
values := []any{
@@ -259,6 +283,9 @@ func (s *LivekitServer) Start() error {
if s.config.Prometheus.Port != 0 {
values = append(values, "portPrometheus", s.config.Prometheus.Port)
}
if s.config.DebugHandler.Port != 0 {
values = append(values, "portDebugHandler", s.config.DebugHandler.Port)
}
if s.config.Region != "" {
values = append(values, "region", s.config.Region)
}
@@ -271,6 +298,10 @@ func (s *LivekitServer) Start() error {
go s.promServer.Serve(promLn)
}
for _, debugLn := range debugListeners {
go s.debugServer.Serve(debugLn)
}
if err := s.signalServer.Start(); err != nil {
return err
}
@@ -302,6 +333,9 @@ func (s *LivekitServer) Start() error {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
_ = s.httpServer.Shutdown(ctx)
if s.debugServer != nil {
_ = s.debugServer.Shutdown(ctx)
}
if s.turnServer != nil {
_ = s.turnServer.Close()
+3
View File
@@ -28,6 +28,7 @@ import (
"strconv"
"strings"
"sync"
"time"
"github.com/ua-parser/uap-go/uaparser"
"gopkg.in/yaml.v3"
@@ -289,6 +290,7 @@ type ValidateConnectRequestParams struct {
type ValidateConnectRequestResult struct {
roomName livekit.RoomName
grants *auth.ClaimGrants
tokenExpiresAt time.Time
region string
createRoomRequest *livekit.CreateRoomRequest
}
@@ -402,6 +404,7 @@ func ValidateConnectRequest(
}
res.grants = claims
res.tokenExpiresAt = GetTokenExpiresAt(r.Context())
return res, http.StatusOK, nil
}
-4
View File
@@ -29,10 +29,6 @@ import (
"os"
)
import (
_ "net/http/pprof"
)
// Injectors from wire.go:
func InitializeServer(conf *config.Config, currentNode routing.LocalNode) (*LivekitServer, error) {
+1
View File
@@ -135,6 +135,7 @@ func (t *telemetryService) ParticipantActive(
prometheus.AddParticipant()
}
worker.SetConnected()
prometheus.IncrementParticipantRtcActive(1)
ev := newParticipantEvent(livekit.AnalyticsEventType_PARTICIPANT_ACTIVE, room, participant)
ev.ClientMeta = clientMeta
+66 -60
View File
@@ -149,40 +149,43 @@ func GetNodeStats(nodeStartedAt int64, prevStats []*livekit.NodeStats, rateInter
promSysPacketGauge.WithLabelValues("dropped").Set(float64(sysDroppedPackets - sysDroppedPacketsStart))
stats := &livekit.NodeStats{
StartedAt: nodeStartedAt,
UpdatedAt: time.Now().Unix(),
NumRooms: roomCurrent.Load(),
NumClients: participantCurrent.Load(),
NumTracksIn: trackPublishedCurrent.Load(),
NumTracksOut: trackSubscribedCurrent.Load(),
NumTrackPublishAttempts: trackPublishAttempts.Load(),
NumTrackPublishSuccess: trackPublishSuccess.Load(),
NumTrackPublishCancels: trackPublishCancels.Load(),
NumTrackSubscribeAttempts: trackSubscribeAttempts.Load(),
NumTrackSubscribeSuccess: trackSubscribeSuccess.Load(),
NumTrackSubscribeCancels: trackSubscribeCancels.Load(),
BytesIn: bytesIn.Load(),
BytesOut: bytesOut.Load(),
PacketsIn: packetsIn.Load(),
PacketsOut: packetsOut.Load(),
RetransmitBytesOut: retransmitBytes.Load(),
RetransmitPacketsOut: retransmitPackets.Load(),
NackTotal: nackTotal.Load(),
ParticipantSignalConnected: participantSignalConnected.Load(),
ParticipantRtcInit: participantRTCInit.Load(),
ParticipantRtcConnected: participantRTCConnected.Load(),
ParticipantRtcCanceled: participantRTCCanceled.Load(),
ForwardLatency: forwardLatency.Load(),
ForwardJitter: forwardJitter.Load(),
NumCpus: uint32(cpuStats.NumCPU()), // this will round down to the nearest integer
CpuLoad: float32(cpuStats.GetCPULoad()),
MemoryTotal: memTotal,
MemoryUsed: memUsed,
LoadAvgLast1Min: float32(loadAvg.Loadavg1),
LoadAvgLast5Min: float32(loadAvg.Loadavg5),
LoadAvgLast15Min: float32(loadAvg.Loadavg15),
SysPacketsOut: sysPackets,
SysPacketsDropped: sysDroppedPackets,
StartedAt: nodeStartedAt,
UpdatedAt: time.Now().Unix(),
NumRooms: roomCurrent.Load(),
NumClients: participantCurrent.Load(),
NumTracksIn: trackPublishedCurrent.Load(),
NumTracksOut: trackSubscribedCurrent.Load(),
NumTrackPublishAttempts: trackPublishAttempts.Load(),
NumTrackPublishSuccess: trackPublishSuccess.Load(),
NumTrackPublishCancels: trackPublishCancels.Load(),
NumTrackSubscribeAttempts: trackSubscribeAttempts.Load(),
NumTrackSubscribeSuccess: trackSubscribeSuccess.Load(),
NumTrackSubscribeCancels: trackSubscribeCancels.Load(),
BytesIn: bytesIn.Load(),
BytesOut: bytesOut.Load(),
PacketsIn: packetsIn.Load(),
PacketsOut: packetsOut.Load(),
RetransmitBytesOut: retransmitBytes.Load(),
RetransmitPacketsOut: retransmitPackets.Load(),
NackTotal: nackTotal.Load(),
ParticipantSignalConnected: participantSignalConnected.Load(),
ParticipantSignalFailed: participantSignalFailed.Load(),
ParticipantSignalValidationFailed: participantSignalValidationFailed.Load(),
ParticipantRtcInit: participantRTCInit.Load(),
ParticipantRtcConnected: participantRTCConnected.Load(),
ParticipantRtcCanceled: participantRTCCanceled.Load(),
ParticipantRtcActive: participantRTCActive.Load(),
ForwardLatency: forwardLatency.Load(),
ForwardJitter: forwardJitter.Load(),
NumCpus: uint32(cpuStats.NumCPU()), // this will round down to the nearest integer
CpuLoad: float32(cpuStats.GetCPULoad()),
MemoryTotal: memTotal,
MemoryUsed: memUsed,
LoadAvgLast1Min: float32(loadAvg.Loadavg1),
LoadAvgLast5Min: float32(loadAvg.Loadavg5),
LoadAvgLast15Min: float32(loadAvg.Loadavg15),
SysPacketsOut: sysPackets,
SysPacketsDropped: sysDroppedPackets,
}
for _, rateInterval := range rateIntervals {
@@ -239,32 +242,35 @@ func getNodeStatsRate(statsHistory []*livekit.NodeStats) *livekit.NodeStatsRate
earlier := statsHistory[0]
later := statsHistory[len(statsHistory)-1]
rate := &livekit.NodeStatsRate{
StartedAt: earlier.UpdatedAt,
EndedAt: later.UpdatedAt,
Duration: elapsed,
BytesIn: perSec(earlier.BytesIn, later.BytesIn, elapsed),
BytesOut: perSec(earlier.BytesOut, later.BytesOut, elapsed),
PacketsIn: perSec(earlier.PacketsIn, later.PacketsIn, elapsed),
PacketsOut: perSec(earlier.PacketsOut, later.PacketsOut, elapsed),
RetransmitBytesOut: perSec(earlier.RetransmitBytesOut, later.RetransmitBytesOut, elapsed),
RetransmitPacketsOut: perSec(earlier.RetransmitPacketsOut, later.RetransmitPacketsOut, elapsed),
NackTotal: perSec(earlier.NackTotal, later.NackTotal, elapsed),
ParticipantSignalConnected: perSec(earlier.ParticipantSignalConnected, later.ParticipantSignalConnected, elapsed),
ParticipantRtcInit: perSec(earlier.ParticipantRtcInit, later.ParticipantRtcInit, elapsed),
ParticipantRtcConnected: perSec(earlier.ParticipantRtcConnected, later.ParticipantRtcConnected, elapsed),
ParticipantRtcCanceled: perSec(earlier.ParticipantRtcCanceled, later.ParticipantRtcCanceled, elapsed),
SysPacketsOut: perSec(uint64(earlier.SysPacketsOut), uint64(later.SysPacketsOut), elapsed),
SysPacketsDropped: perSec(uint64(earlier.SysPacketsDropped), uint64(later.SysPacketsDropped), elapsed),
TrackPublishAttempts: perSec(uint64(earlier.NumTrackPublishAttempts), uint64(later.NumTrackPublishAttempts), elapsed),
TrackPublishSuccess: perSec(uint64(earlier.NumTrackPublishSuccess), uint64(later.NumTrackPublishSuccess), elapsed),
TrackPublishCancels: perSec(uint64(earlier.NumTrackPublishCancels), uint64(later.NumTrackPublishCancels), elapsed),
TrackSubscribeAttempts: perSec(uint64(earlier.NumTrackSubscribeAttempts), uint64(later.NumTrackSubscribeAttempts), elapsed),
TrackSubscribeSuccess: perSec(uint64(earlier.NumTrackSubscribeSuccess), uint64(later.NumTrackSubscribeSuccess), elapsed),
TrackSubscribeCancels: perSec(uint64(earlier.NumTrackSubscribeCancels), uint64(later.NumTrackSubscribeCancels), elapsed),
CpuLoad: cpuLoad / float32(elapsed),
MemoryLoad: memoryLoad / float32(elapsed),
MemoryUsed: memoryUsed / float32(elapsed),
MemoryTotal: memoryTotal / float32(elapsed),
StartedAt: earlier.UpdatedAt,
EndedAt: later.UpdatedAt,
Duration: elapsed,
BytesIn: perSec(earlier.BytesIn, later.BytesIn, elapsed),
BytesOut: perSec(earlier.BytesOut, later.BytesOut, elapsed),
PacketsIn: perSec(earlier.PacketsIn, later.PacketsIn, elapsed),
PacketsOut: perSec(earlier.PacketsOut, later.PacketsOut, elapsed),
RetransmitBytesOut: perSec(earlier.RetransmitBytesOut, later.RetransmitBytesOut, elapsed),
RetransmitPacketsOut: perSec(earlier.RetransmitPacketsOut, later.RetransmitPacketsOut, elapsed),
NackTotal: perSec(earlier.NackTotal, later.NackTotal, elapsed),
ParticipantSignalConnected: perSec(earlier.ParticipantSignalConnected, later.ParticipantSignalConnected, elapsed),
ParticipantSignalFailed: perSec(earlier.ParticipantSignalFailed, later.ParticipantSignalFailed, elapsed),
ParticipantSignalValidationFailed: perSec(earlier.ParticipantSignalValidationFailed, later.ParticipantSignalValidationFailed, elapsed),
ParticipantRtcInit: perSec(earlier.ParticipantRtcInit, later.ParticipantRtcInit, elapsed),
ParticipantRtcConnected: perSec(earlier.ParticipantRtcConnected, later.ParticipantRtcConnected, elapsed),
ParticipantRtcCanceled: perSec(earlier.ParticipantRtcCanceled, later.ParticipantRtcCanceled, elapsed),
ParticipantRtcActive: perSec(earlier.ParticipantRtcActive, later.ParticipantRtcActive, elapsed),
SysPacketsOut: perSec(uint64(earlier.SysPacketsOut), uint64(later.SysPacketsOut), elapsed),
SysPacketsDropped: perSec(uint64(earlier.SysPacketsDropped), uint64(later.SysPacketsDropped), elapsed),
TrackPublishAttempts: perSec(uint64(earlier.NumTrackPublishAttempts), uint64(later.NumTrackPublishAttempts), elapsed),
TrackPublishSuccess: perSec(uint64(earlier.NumTrackPublishSuccess), uint64(later.NumTrackPublishSuccess), elapsed),
TrackPublishCancels: perSec(uint64(earlier.NumTrackPublishCancels), uint64(later.NumTrackPublishCancels), elapsed),
TrackSubscribeAttempts: perSec(uint64(earlier.NumTrackSubscribeAttempts), uint64(later.NumTrackSubscribeAttempts), elapsed),
TrackSubscribeSuccess: perSec(uint64(earlier.NumTrackSubscribeSuccess), uint64(later.NumTrackSubscribeSuccess), elapsed),
TrackSubscribeCancels: perSec(uint64(earlier.NumTrackSubscribeCancels), uint64(later.NumTrackSubscribeCancels), elapsed),
CpuLoad: cpuLoad / float32(elapsed),
MemoryLoad: memoryLoad / float32(elapsed),
MemoryUsed: memoryUsed / float32(elapsed),
MemoryTotal: memoryTotal / float32(elapsed),
}
return rate
}
+35 -17
View File
@@ -36,19 +36,22 @@ const (
)
var (
bytesIn atomic.Uint64
bytesOut atomic.Uint64
packetsIn atomic.Uint64
packetsOut atomic.Uint64
nackTotal atomic.Uint64
retransmitBytes atomic.Uint64
retransmitPackets atomic.Uint64
participantSignalConnected atomic.Uint64
participantRTCConnected atomic.Uint64
participantRTCInit atomic.Uint64
participantRTCCanceled atomic.Uint64
forwardLatency atomic.Uint32
forwardJitter atomic.Uint32
bytesIn atomic.Uint64
bytesOut atomic.Uint64
packetsIn atomic.Uint64
packetsOut atomic.Uint64
nackTotal atomic.Uint64
retransmitBytes atomic.Uint64
retransmitPackets atomic.Uint64
participantSignalConnected atomic.Uint64
participantSignalFailed atomic.Uint64
participantSignalValidationFailed atomic.Uint64
participantRTCConnected atomic.Uint64
participantRTCInit atomic.Uint64
participantRTCCanceled atomic.Uint64
participantRTCActive atomic.Uint64
forwardLatency atomic.Uint32
forwardJitter atomic.Uint32
promPacketLabels = []string{"direction", "transmission", "country"}
promPacketTotal *prometheus.CounterVec
@@ -300,9 +303,17 @@ func IncrementParticipantJoin(join uint32) {
}
}
func IncrementParticipantJoinFail(join uint32) {
if join > 0 {
promParticipantJoin.WithLabelValues("signal_failed").Add(float64(join))
func IncrementParticipantJoinFail(fail uint32) {
if fail > 0 {
participantSignalFailed.Add(uint64(fail))
promParticipantJoin.WithLabelValues("signal_failed").Add(float64(fail))
}
}
func IncrementParticipantJoinValidationFail(validationFail uint32) {
if validationFail > 0 {
participantSignalValidationFailed.Add(uint64(validationFail))
promParticipantJoin.WithLabelValues("signal_validation_failed").Add(float64(validationFail))
}
}
@@ -320,9 +331,16 @@ func IncrementParticipantRtcConnected(join uint32) {
}
}
func IncrementParticipantRtcActive(active uint32) {
if active > 0 {
participantRTCActive.Add(uint64(active))
promParticipantJoin.WithLabelValues("rtc_active").Add(float64(active))
}
}
func IncrementParticipantRtcCanceled(numCancels uint64) {
if numCancels > 0 {
participantRTCConnected.Add(numCancels)
participantRTCCanceled.Add(numCancels)
promParticipantJoin.WithLabelValues("rtc_canceled").Add(float64(numCancels))
}
}
+13
View File
@@ -49,6 +49,8 @@ var (
promSessionStartTime *prometheus.HistogramVec
promSessionDuration *prometheus.HistogramVec
promPubSubTime *prometheus.HistogramVec
promPeerConnection *prometheus.CounterVec
)
func initRoomStats(nodeID string, nodeType livekit.NodeType) {
@@ -118,6 +120,12 @@ func initRoomStats(nodeID string, nodeType livekit.NodeType) {
ConstLabels: prometheus.Labels{"node_id": nodeID, "node_type": nodeType.String()},
Buckets: []float64{100, 200, 500, 700, 1000, 5000, 10000},
}, append(promStreamLabels, "sdk", "kind", "count"))
promPeerConnection = prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: livekitNamespace,
Subsystem: "peer_connection",
Name: "state",
ConstLabels: prometheus.Labels{"node_id": nodeID, "node_type": nodeType.String()},
}, []string{"transport", "state"})
prometheus.MustRegister(promRoomCurrent)
prometheus.MustRegister(promRoomDuration)
@@ -129,6 +137,7 @@ func initRoomStats(nodeID string, nodeType livekit.NodeType) {
prometheus.MustRegister(promSessionStartTime)
prometheus.MustRegister(promSessionDuration)
prometheus.MustRegister(promPubSubTime)
prometheus.MustRegister(promPeerConnection)
}
func RoomStarted() {
@@ -269,3 +278,7 @@ func RecordSessionStartTime(protocolVersion int, d time.Duration) {
func RecordSessionDuration(protocolVersion int, d time.Duration) {
promSessionDuration.WithLabelValues(strconv.Itoa(protocolVersion)).Observe(float64(d.Milliseconds()))
}
func RecordPeerConnectionState(transport livekit.SignalTarget, state string) {
promPeerConnection.WithLabelValues(transport.String(), state).Inc()
}
+18 -4
View File
@@ -765,13 +765,21 @@ func TestSingleNodeUpdateSubscriptionPermissions(t *testing.T) {
SetIdentity("sub")
token, err := at.ToJWT()
require.NoError(t, err)
sub := createRTCClientWithToken(token, defaultServerPort, testRTCServicePath, nil)
sub := createRTCClientWithToken(token, defaultServerPort, testRTCServicePath, &testclient.Options{
AutoSubscribe: true,
AutoSubscribeDataTrack: true,
})
waitUntilConnected(t, pub, sub)
writers := publishTracksForClients(t, pub)
defer stopWriters(writers...)
// publish a data track as well
dtw, err := pub.PublishDataTrack()
require.NoError(t, err)
defer dtw.Stop()
// wait sub receives tracks
testutils.WithTimeout(t, func() string {
pubRemote := sub.GetRemoteParticipant(pub.ID())
@@ -784,6 +792,10 @@ func TestSingleNodeUpdateSubscriptionPermissions(t *testing.T) {
return ""
})
// no subscriptions should have been made while canSubscribe is false
require.Empty(t, sub.SubscribedTracks()[pub.ID()])
require.Empty(t, sub.SubscribedDataTracks()[pub.ID()])
// set permissions out of band
ctx := contextWithToken(adminRoomToken(testRoom))
_, err = roomClient.UpdateParticipant(ctx, &livekit.UpdateParticipantRequest{
@@ -798,11 +810,13 @@ func TestSingleNodeUpdateSubscriptionPermissions(t *testing.T) {
testutils.WithTimeout(t, func() string {
tracks := sub.SubscribedTracks()[pub.ID()]
if len(tracks) == 2 {
return ""
} else {
if len(tracks) != 2 {
return fmt.Sprintf("expected 2 tracks subscribed, actual: %d", len(tracks))
}
if len(sub.SubscribedDataTracks()[pub.ID()]) != 1 {
return "expected data track to be subscribed"
}
return ""
})
})
}
+1 -1
View File
@@ -14,4 +14,4 @@
package version
const Version = "1.12.0"
const Version = "1.13.1"