From 4ec179438f013f5a321b8cbf22629bb879c1934b Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Thu, 4 Jun 2026 10:18:55 -0700 Subject: [PATCH 01/12] =?UTF-8?q?deps:=20upgrade=20xsync=20v3=E2=86=92v4?= =?UTF-8?q?=20and=20go-jose=20v3=E2=86=92v4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xsync v4: MapOf is an alias for Map; the LoadOrCompute callback signature changed to return (V, bool), so existing call sites pass false as the cancel flag. go-jose v4: jwt.ParseSigned now requires an explicit signature- algorithm allowlist (HS256, matching what we sign with), and Builder.CompactSerialize was renamed to Serialize. v4 also enforces RFC 7518's HMAC key-size minimum (HS256 ≥ 32 bytes); secrets issued via utils.RandomSecret already satisfy this, but any deployment using a custom shorter secret will now hit ErrInvalidKeySize. --- auth/accesstoken.go | 6 +- auth/accesstoken_test.go | 11 +- auth/verifier.go | 7 +- auth/verifier_test.go | 10 +- go.mod | 92 +++++++-------- go.sum | 238 ++++++++++++++++----------------------- logger/logger.go | 18 +-- logger/proto.go | 8 +- rpc/logging.go | 10 +- 9 files changed, 181 insertions(+), 219 deletions(-) diff --git a/auth/accesstoken.go b/auth/accesstoken.go index 2bb3b9291..641ee0505 100644 --- a/auth/accesstoken.go +++ b/auth/accesstoken.go @@ -17,8 +17,8 @@ package auth import ( "time" - "github.com/go-jose/go-jose/v3" - "github.com/go-jose/go-jose/v3/jwt" + "github.com/go-jose/go-jose/v4" + "github.com/go-jose/go-jose/v4/jwt" "github.com/livekit/protocol/livekit" ) @@ -191,5 +191,5 @@ func (t *AccessToken) ToJWT() (string, error) { Expiry: jwt.NewNumericDate(time.Now().Add(validFor)), Subject: t.grant.Identity, } - return jwt.Signed(sig).Claims(cl).Claims(&t.grant).CompactSerialize() + return jwt.Signed(sig).Claims(cl).Claims(&t.grant).Serialize() } diff --git a/auth/accesstoken_test.go b/auth/accesstoken_test.go index 372a70fde..f0357b5ea 100644 --- a/auth/accesstoken_test.go +++ b/auth/accesstoken_test.go @@ -21,7 +21,8 @@ import ( "testing" "time" - "github.com/go-jose/go-jose/v3/jwt" + "github.com/go-jose/go-jose/v4" + "github.com/go-jose/go-jose/v4/jwt" "github.com/stretchr/testify/require" "github.com/livekit/protocol/livekit" @@ -59,7 +60,7 @@ func TestAccessToken(t *testing.T) { require.Len(t, strings.Split(value, "."), 3) // ensure it's a valid JWT - token, err := jwt.ParseSigned(value) + token, err := jwt.ParseSigned(value, []jose.SignatureAlgorithm{jose.HS256}) require.NoError(t, err) decodedGrant := ClaimGrants{} @@ -79,7 +80,7 @@ func TestAccessToken(t *testing.T) { SetVideoGrant(&VideoGrant{RoomJoin: true, Room: "myroom"}). ToJWT() require.NoError(t, err) - token, err := jwt.ParseSigned(value) + token, err := jwt.ParseSigned(value, []jose.SignatureAlgorithm{jose.HS256}) require.NoError(t, err) decodedGrant := ClaimGrants{} @@ -97,7 +98,7 @@ func TestAccessToken(t *testing.T) { SetVideoGrant(videoGrant) value, err := at.ToJWT() require.NoError(t, err) - token, err := jwt.ParseSigned(value) + token, err := jwt.ParseSigned(value, []jose.SignatureAlgorithm{jose.HS256}) require.NoError(t, err) claim := jwt.Claims{} @@ -135,7 +136,7 @@ func TestAccessToken(t *testing.T) { require.NoError(t, err) // Parse and verify the token - token, err := jwt.ParseSigned(value) + token, err := jwt.ParseSigned(value, []jose.SignatureAlgorithm{jose.HS256}) require.NoError(t, err) decodedGrant := ClaimGrants{} diff --git a/auth/verifier.go b/auth/verifier.go index bcaa22bff..23d0cf8ab 100644 --- a/auth/verifier.go +++ b/auth/verifier.go @@ -17,9 +17,12 @@ package auth import ( "time" - "github.com/go-jose/go-jose/v3/jwt" + "github.com/go-jose/go-jose/v4" + "github.com/go-jose/go-jose/v4/jwt" ) +var allowedSignatureAlgorithms = []jose.SignatureAlgorithm{jose.HS256} + type APIKeyTokenVerifier struct { token *jwt.JSONWebToken identity string @@ -28,7 +31,7 @@ type APIKeyTokenVerifier struct { // ParseAPIToken parses an encoded JWT token and func ParseAPIToken(raw string) (*APIKeyTokenVerifier, error) { - tok, err := jwt.ParseSigned(raw) + tok, err := jwt.ParseSigned(raw, allowedSignatureAlgorithms) if err != nil { return nil, err } diff --git a/auth/verifier_test.go b/auth/verifier_test.go index c608f340f..123314bd7 100644 --- a/auth/verifier_test.go +++ b/auth/verifier_test.go @@ -18,9 +18,9 @@ import ( "testing" "time" - "github.com/go-jose/go-jose/v3" - "github.com/go-jose/go-jose/v3/json" - "github.com/go-jose/go-jose/v3/jwt" + "github.com/go-jose/go-jose/v4" + "github.com/go-jose/go-jose/v4/json" + "github.com/go-jose/go-jose/v4/jwt" "github.com/stretchr/testify/require" "github.com/livekit/protocol/auth" @@ -28,7 +28,7 @@ import ( func TestVerifier(t *testing.T) { apiKey := "APID3B67uxk4Nj2GKiRPibAZ9" - secret := "YHC-CUhbQhGeVCaYgn1BNA++" + secret := "7uxiM/OjTlB+X4Ep6TrZfi+PK2A/ekuPwiw" accessToken := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MDg5MzAzMDgsImlzcyI6IkFQSUQzQjY3dXhrNE5qMkdLaVJQaWJBWjkiLCJuYmYiOjE2MDg5MjY3MDgsInJvb21fam9pbiI6dHJ1ZSwicm9vbV9zaWQiOiJteWlkIiwic3ViIjoiQVBJRDNCNjd1eGs0TmoyR0tpUlBpYkFaOSJ9.cmHEBq0MLyRqphmVLM2cLXg5ao5Sro7am8yXhcYKcwE" t.Run("cannot decode with incorrect key", func(t *testing.T) { v, err := auth.ParseAPIToken(accessToken) @@ -127,7 +127,7 @@ func TestVerifier(t *testing.T) { "someFutureRoomConfigField": "future-value", }, } - token, err := jwt.Signed(sig).Claims(claims).CompactSerialize() + token, err := jwt.Signed(sig).Claims(claims).Serialize() require.NoError(t, err) v, err := auth.ParseAPIToken(token) diff --git a/go.mod b/go.mod index 4a531e084..b51df89eb 100644 --- a/go.mod +++ b/go.mod @@ -3,13 +3,13 @@ module github.com/livekit/protocol go 1.26 require ( - buf.build/go/protoyaml v0.6.0 + buf.build/go/protoyaml v0.7.0 github.com/benbjohnson/clock v1.3.5 - github.com/dennwc/iters v1.1.0 + github.com/dennwc/iters v1.2.2 github.com/frostbyte73/core v0.1.1 - github.com/fsnotify/fsnotify v1.9.0 - github.com/gammazero/deque v1.1.0 - github.com/go-jose/go-jose/v3 v3.0.5 + github.com/fsnotify/fsnotify v1.10.1 + github.com/gammazero/deque v1.2.1 + github.com/go-jose/go-jose/v4 v4.1.4 github.com/go-logr/logr v1.4.3 github.com/hashicorp/go-retryablehttp v0.7.7 github.com/jxskiss/base62 v1.1.0 @@ -20,80 +20,80 @@ require ( github.com/maxbrunsfeld/counterfeiter/v6 v6.11.1 github.com/nyaruka/phonenumbers v1.6.5 github.com/pion/logging v0.2.4 - github.com/pion/sdp/v3 v3.0.14 - github.com/pion/webrtc/v4 v4.1.2 + github.com/pion/sdp/v3 v3.0.18 + github.com/pion/webrtc/v4 v4.2.14 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.22.0 github.com/prometheus/procfs v0.16.1 - github.com/puzpuzpuz/xsync/v3 v3.5.1 - github.com/redis/go-redis/v9 v9.11.0 + github.com/puzpuzpuz/xsync/v4 v4.5.0 + github.com/redis/go-redis/v9 v9.20.0 github.com/stretchr/testify v1.11.1 github.com/twitchtv/twirp v8.1.3+incompatible - github.com/zeebo/xxh3 v1.0.2 - go.opentelemetry.io/otel v1.43.0 + github.com/zeebo/xxh3 v1.1.0 + go.opentelemetry.io/otel v1.44.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 go.opentelemetry.io/otel/sdk v1.43.0 - go.opentelemetry.io/otel/trace v1.43.0 + go.opentelemetry.io/otel/trace v1.44.0 go.uber.org/atomic v1.11.0 go.uber.org/multierr v1.11.0 - go.uber.org/zap v1.27.0 + go.uber.org/zap v1.28.0 go.uber.org/zap/exp v0.3.0 - golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b - golang.org/x/mod v0.34.0 - golang.org/x/sys v0.43.0 - golang.org/x/text v0.36.0 - google.golang.org/genproto/googleapis/rpc v0.0.0-20260427160629-7cedc36a6bc4 - google.golang.org/grpc v1.80.0 + golang.org/x/exp v0.0.0-20260603202125-055de637280b + golang.org/x/mod v0.36.0 + golang.org/x/sys v0.45.0 + golang.org/x/text v0.37.0 + google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa + google.golang.org/grpc v1.81.1 google.golang.org/protobuf v1.36.11 gopkg.in/yaml.v3 v3.0.1 ) require ( - buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250625184727-c923a0c2a132.1 // indirect - buf.build/go/protovalidate v0.13.1 // indirect - cel.dev/expr v0.25.1 // indirect + buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260415201107-50325440f8f2.1 // indirect + buf.build/go/protovalidate v1.2.0 // indirect + cel.dev/expr v0.25.2 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/google/cel-go v0.25.0 // indirect + github.com/google/cel-go v0.28.1 // indirect github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/klauspost/compress v1.18.0 // indirect - github.com/klauspost/cpuid/v2 v2.2.11 // indirect + github.com/klauspost/compress v1.18.6 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/nats-io/nats.go v1.43.0 // indirect - github.com/nats-io/nkeys v0.4.11 // 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/pion/datachannel v1.5.10 // indirect - github.com/pion/dtls/v3 v3.0.6 // indirect - github.com/pion/ice/v4 v4.0.10 // indirect - github.com/pion/interceptor v0.1.40 // indirect - github.com/pion/mdns/v2 v2.0.7 // indirect + github.com/pion/datachannel v1.6.0 // indirect + github.com/pion/dtls/v3 v3.1.3 // indirect + github.com/pion/ice/v4 v4.2.7 // indirect + github.com/pion/interceptor v0.1.45 // indirect + github.com/pion/mdns/v2 v2.1.0 // indirect github.com/pion/randutil v0.1.0 // indirect - github.com/pion/rtcp v1.2.15 // indirect - github.com/pion/rtp v1.8.19 // indirect - github.com/pion/sctp v1.8.39 // indirect - github.com/pion/srtp/v3 v3.0.6 // indirect - github.com/pion/stun/v3 v3.0.0 // indirect - github.com/pion/transport/v3 v3.0.7 // indirect - github.com/pion/turn/v4 v4.0.2 // indirect + github.com/pion/rtcp v1.2.16 // indirect + github.com/pion/rtp v1.10.2 // indirect + github.com/pion/sctp v1.10.0 // indirect + github.com/pion/srtp/v3 v3.0.11 // indirect + github.com/pion/stun/v3 v3.1.4 // indirect + github.com/pion/transport/v4 v4.0.2 // indirect + github.com/pion/turn/v5 v5.0.8 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.64.0 // indirect - github.com/stoewer/go-strcase v1.3.1 // indirect github.com/wlynxg/anet v0.0.5 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect - go.opentelemetry.io/otel/metric v1.43.0 // indirect + go.opentelemetry.io/otel/metric v1.44.0 // indirect go.opentelemetry.io/proto/otlp v1.10.0 // indirect - golang.org/x/crypto v0.50.0 // indirect - golang.org/x/net v0.53.0 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/crypto v0.52.0 // indirect + golang.org/x/net v0.55.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/tools v0.43.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260427160629-7cedc36a6bc4 // indirect + golang.org/x/time v0.15.0 // indirect + golang.org/x/tools v0.45.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect ) diff --git a/go.sum b/go.sum index af7e08704..267f5e595 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,11 @@ -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250625184727-c923a0c2a132.1 h1:6tCo3lsKNLqUjRPhyc8JuYWYUiQkulufxSDOfG1zgWQ= -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250625184727-c923a0c2a132.1/go.mod h1:avRlCjnFzl98VPaeCtJ24RrV/wwHFzB8sWXhj26+n/U= -buf.build/go/protovalidate v0.13.1 h1:6loHDTWdY/1qmqmt1MijBIKeN4T9Eajrqb9isT1W1s8= -buf.build/go/protovalidate v0.13.1/go.mod h1:C/QcOn/CjXRn5udUwYBiLs8y1TGy7RS+GOSKqjS77aU= -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= +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= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= @@ -20,6 +20,8 @@ github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/brianvoe/gofakeit/v6 v6.28.0 h1:Xib46XXuQfmlLS2EXRuJpqcw8St6qSZz75OUo0tgAW4= +github.com/brianvoe/gofakeit/v6 v6.28.0/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs= 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= @@ -32,13 +34,10 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8= github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= -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/dennwc/iters v1.1.0 h1:PsS3DbOU7GxSUQO0e7SGmzHkPhtwOlwbqggJ++Bgnr8= -github.com/dennwc/iters v1.1.0/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/dennwc/iters v1.2.2 h1:XH2/Etihiy9ZvPOVCR+icQXeYlhbvS7k0qro4x/2qQo= +github.com/dennwc/iters v1.2.2/go.mod h1:M9KuuMBeyEXYTmB7EnI9SCyALFCmPWOIxn5W1L0CjGg= github.com/docker/cli v26.1.4+incompatible h1:I8PHdc0MtxEADqYJZvhBrW9bo8gawKwwenxRM7/rLu8= github.com/docker/cli v26.1.4+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= @@ -51,12 +50,12 @@ github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= 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/gammazero/deque v1.1.0 h1:OyiyReBbnEG2PP0Bnv1AASLIYvyKqIFN5xfl1t8oGLo= -github.com/gammazero/deque v1.1.0/go.mod h1:JVrR+Bj1NMQbPnYclvDlvSX0nVGReLrQZ0aUMuWLctg= -github.com/go-jose/go-jose/v3 v3.0.5 h1:BLLJWbC4nMZOfuPVxoZIxeYsn6Nl2r1fITaJ78UQlVQ= -github.com/go-jose/go-jose/v3 v3.0.5/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= +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/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= +github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -66,9 +65,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 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.25.0 h1:jsFw9Fhn+3y2kBbltZR4VEz5xKkcIFRPDnuEzAGv5GY= -github.com/google/cel-go v0.25.0/go.mod h1:hjEb6r5SuOSlhCHmFoLzu8HGCERvIsDAbxDAyNU/MmI= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +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.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= @@ -85,10 +83,10 @@ github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISH github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= 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/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= -github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU= -github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +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.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -115,10 +113,10 @@ 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/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.43.0 h1:uRFZ2FEoRvP64+UUhaTokyS18XBCR/xM2vQZKO4i8ug= -github.com/nats-io/nats.go v1.43.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g= -github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0= -github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE= +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= @@ -133,38 +131,40 @@ github.com/opencontainers/runc v1.1.13 h1:98S2srgG9vw0zWcDpFMn5TRrh8kLxa/5OFUstu github.com/opencontainers/runc v1.1.13/go.mod h1:R016aXacfp/gwQBYw2FDGa9m+n6atbLWrYY8hNMT/sA= github.com/ory/dockertest/v3 v3.11.0 h1:OiHcxKAvSDUwsEVh2BjxQQc/5EHz9n0va9awCtNGuyA= github.com/ory/dockertest/v3 v3.11.0/go.mod h1:VIPxS1gwT9NpPOrfD3rACs8Y9Z7yhzO4SB194iUDnUI= -github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o= -github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M= -github.com/pion/dtls/v3 v3.0.6 h1:7Hkd8WhAJNbRgq9RgdNh1aaWlZlGpYTzdqjy9x9sK2E= -github.com/pion/dtls/v3 v3.0.6/go.mod h1:iJxNQ3Uhn1NZWOMWlLxEEHAN5yX7GyPvvKw04v9bzYU= -github.com/pion/ice/v4 v4.0.10 h1:P59w1iauC/wPk9PdY8Vjl4fOFL5B+USq1+xbDcN6gT4= -github.com/pion/ice/v4 v4.0.10/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw= -github.com/pion/interceptor v0.1.40 h1:e0BjnPcGpr2CFQgKhrQisBU7V3GXK6wrfYrGYaU6Jq4= -github.com/pion/interceptor v0.1.40/go.mod h1:Z6kqH7M/FYirg3frjGJ21VLSRJGBXB/KqaTIrdqnOic= +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.3 h1:OA6J5UCeA8DvRXD8ofaMnlNPXN3ISBLHHJ9P8SWL09E= +github.com/pion/dtls/v3 v3.1.3/go.mod h1:GEwid4EzCcakfrNvHXM7bs6ci2mASI5Y5Q4tbtLFuWs= +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.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM= -github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA= +github.com/pion/mdns/v2 v2.1.0 h1:3IJ9+Xio6tWYjhN6WwuY142P/1jA0D5ERaIqawg/fOY= +github.com/pion/mdns/v2 v2.1.0/go.mod h1:pcez23GdynwcfRU1977qKU0mDxSeucttSHbCSfFOd9A= 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.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo= -github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0= -github.com/pion/rtp v1.8.19 h1:jhdO/3XhL/aKm/wARFVmvTfq0lC/CvN1xwYKmduly3c= -github.com/pion/rtp v1.8.19/go.mod h1:bAu2UFKScgzyFqvUKmbvzSdPr+NGbZtv6UB2hesqXBk= -github.com/pion/sctp v1.8.39 h1:PJma40vRHa3UTO3C4MyeJDQ+KIobVYRZQZ0Nt7SjQnE= -github.com/pion/sctp v1.8.39/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE= -github.com/pion/sdp/v3 v3.0.14 h1:1h7gBr9FhOWH5GjWWY5lcw/U85MtdcibTyt/o6RxRUI= -github.com/pion/sdp/v3 v3.0.14/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E= -github.com/pion/srtp/v3 v3.0.6 h1:E2gyj1f5X10sB/qILUGIkL4C2CqK269Xq167PbGCc/4= -github.com/pion/srtp/v3 v3.0.6/go.mod h1:BxvziG3v/armJHAaJ87euvkhHqWe9I7iiOy50K2QkhY= -github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw= -github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU= -github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0= -github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= -github.com/pion/turn/v4 v4.0.2 h1:ZqgQ3+MjP32ug30xAbD6Mn+/K4Sxi3SdNOTFf+7mpps= -github.com/pion/turn/v4 v4.0.2/go.mod h1:pMMKP/ieNAG/fN5cZiN4SDuyKsXtNTr0ccN7IToA1zs= -github.com/pion/webrtc/v4 v4.1.2 h1:mpuUo/EJ1zMNKGE79fAdYNFZBX790KE7kQQpLMjjR54= -github.com/pion/webrtc/v4 v4.1.2/go.mod h1:xsCXiNAmMEjIdFxAYU0MbB3RwRieJsegSB2JZsGN+8U= +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.2 h1:l+f6tTDcAH6xwepaAoW791ddhuYsJlqRATOzirO04Mo= +github.com/pion/rtp v1.10.2/go.mod h1:Au8fc6cEByy8RLTwKTQTEeQqDB/SJDxwL4mZuxYA5Pk= +github.com/pion/sctp v1.10.0 h1:qeoD6swF/2M5bYRcAGayqSbTKX3m4AW29CiQxG1+Pfg= +github.com/pion/sctp v1.10.0/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.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.2 h1:ifYlPqNwsy6aKQ9y8yzxXlHae5431ZrH2avkD/Rn6Tk= +github.com/pion/transport/v4 v4.0.2/go.mod h1:06hFI+jCFcok2X2MekVufNZ/uzNZXivGBPfviSVcjgM= +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.14 h1:Q6zMs+fSDsYuhZcNlvFGBxCOMHVV9oYcDa6O9/HIGTc= +github.com/pion/webrtc/v4 v4.2.14/go.mod h1:87NVKP86+g4OMrRxWhjWfUjeXP4JrV6RTlUrIW+/Jak= 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= @@ -177,10 +177,12 @@ github.com/prometheus/common v0.64.0 h1:pdZeA+g617P7oGv1CzdTzyeShxAGrTBsolKNOLQP github.com/prometheus/common v0.64.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= -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.11.0 h1:E3S08Gl/nJNn5vkxd2i78wZxWAPNZgUNTp8WIJUAiIs= -github.com/redis/go-redis/v9 v9.11.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= +github.com/puzpuzpuz/xsync/v4 v4.5.0 h1:vOSWu6b57/emh+L/Cw0BeQfvxa/cogFywXHeGUxQxAg= +github.com/puzpuzpuz/xsync/v4 v4.5.0/go.mod h1:VJDmTCJMBt8igNxnkQd86r+8KUeN1quSfNKu5bLYFQo= +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.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8= @@ -189,15 +191,6 @@ 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/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs= -github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/twitchtv/twirp v8.1.3+incompatible h1:+F4TdErPgSUbMZMwp13Q/KgDVuI7HJXP61mNV3/7iuU= @@ -210,27 +203,26 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo 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.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= -github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= -github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= +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 v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= +go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= 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/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.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/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= @@ -239,71 +231,38 @@ 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.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/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= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -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-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= -golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= -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/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -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/sync v0.0.0-20190423024810-112230192c58/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.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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.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.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4= +golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ= +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.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.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/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= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -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/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.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/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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/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.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8= +golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= 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= @@ -311,6 +270,5 @@ 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/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= diff --git a/logger/logger.go b/logger/logger.go index 52654c715..c60d030a3 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -25,7 +25,7 @@ import ( "github.com/go-logr/logr" "github.com/go-logr/logr/funcr" - "github.com/puzpuzpuz/xsync/v3" + "github.com/puzpuzpuz/xsync/v4" "go.uber.org/zap" "go.uber.org/zap/zapcore" @@ -210,8 +210,8 @@ func (c *sharedConfig) ComponentLevel(component string) zap.AtomicLevel { type zapConfig struct { conf *Config sc *sharedConfig - writeEnablers *xsync.MapOf[string, *zaputil.WriteEnabler] - levelEnablers *xsync.MapOf[string, *zaputil.OrLevelEnabler] + writeEnablers *xsync.Map[string, *zaputil.WriteEnabler] + levelEnablers *xsync.Map[string, *zaputil.OrLevelEnabler] tap *zaputil.WriteEnabler } @@ -253,8 +253,8 @@ func FromZapLogger(log *zap.Logger, conf *Config, opts ...ZapLoggerOption) (ZapL zc := &zapConfig{ conf: conf, sc: newSharedConfig(conf), - writeEnablers: xsync.NewMapOf[string, *zaputil.WriteEnabler](), - levelEnablers: xsync.NewMapOf[string, *zaputil.OrLevelEnabler](), + writeEnablers: xsync.NewMap[string, *zaputil.WriteEnabler](), + levelEnablers: xsync.NewMap[string, *zaputil.OrLevelEnabler](), tap: zaputil.NewDiscardWriteEnabler(), } for _, opt := range opts { @@ -299,8 +299,8 @@ func newZapLogger[T zaputil.Encoder[T]](zap *zap.SugaredLogger, zc *zapConfig, e func (l *zapLogger[T]) makeZap() *zap.SugaredLogger { var console *zaputil.WriteEnabler if l.minLevel == nil { - console, _ = l.writeEnablers.LoadOrCompute(l.component, func() *zaputil.WriteEnabler { - return zaputil.NewWriteEnabler(os.Stderr, l.sc.ComponentLevel(l.component)) + console, _ = l.writeEnablers.LoadOrCompute(l.component, func() (*zaputil.WriteEnabler, bool) { + return zaputil.NewWriteEnabler(os.Stderr, l.sc.ComponentLevel(l.component)), false }) } else { enab := zaputil.OrLevelEnabler{l.minLevel, l.sc.ComponentLevel(l.component)} @@ -331,8 +331,8 @@ func (l zapLoggerComponentLeveler[T]) ComponentLevel(component string) zapcore.L component = l.zl.component + "." + component } - enab, _ := l.zl.levelEnablers.LoadOrCompute(component, func() *zaputil.OrLevelEnabler { - return &zaputil.OrLevelEnabler{l.zl.sc.ComponentLevel(component), l.zl.tap} + enab, _ := l.zl.levelEnablers.LoadOrCompute(component, func() (*zaputil.OrLevelEnabler, bool) { + return &zaputil.OrLevelEnabler{l.zl.sc.ComponentLevel(component), l.zl.tap}, false }) return enab } diff --git a/logger/proto.go b/logger/proto.go index e5722a0c5..65e2d797b 100644 --- a/logger/proto.go +++ b/logger/proto.go @@ -26,7 +26,7 @@ import ( "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/types/dynamicpb" - "github.com/puzpuzpuz/xsync/v3" + "github.com/puzpuzpuz/xsync/v4" "github.com/livekit/protocol/livekit/logger" "github.com/livekit/protocol/utils/must" @@ -204,7 +204,7 @@ func marshalProtoBytes(b []byte) string { } } -var redactTemplates = xsync.NewMapOf[string, *template.Template]() +var redactTemplates = xsync.NewMap[string, *template.Template]() func marshalRedacted(f protoreflect.FieldDescriptor, v protoreflect.Value) string { if !proto.HasExtension(f.Options(), logger.E_RedactFormat) { @@ -212,8 +212,8 @@ func marshalRedacted(f protoreflect.FieldDescriptor, v protoreflect.Value) strin } text := proto.GetExtension(f.Options(), logger.E_RedactFormat).(string) - tpl, _ := redactTemplates.LoadOrCompute(text, func() *template.Template { - return template.Must(template.New("format").Parse(text)) + tpl, _ := redactTemplates.LoadOrCompute(text, func() (*template.Template, bool) { + return template.Must(template.New("format").Parse(text)), false }) var b bytes.Buffer diff --git a/rpc/logging.go b/rpc/logging.go index 243de78b1..dca59815f 100644 --- a/rpc/logging.go +++ b/rpc/logging.go @@ -18,7 +18,7 @@ import ( "context" "time" - "github.com/puzpuzpuz/xsync/v3" + "github.com/puzpuzpuz/xsync/v4" "google.golang.org/protobuf/proto" "github.com/livekit/protocol/logger" @@ -26,16 +26,16 @@ import ( ) type loggerCache struct { - m *xsync.MapOf[string, logger.Logger] + m *xsync.Map[string, logger.Logger] } func newLoggerCache() loggerCache { - return loggerCache{m: xsync.NewMapOf[string, logger.Logger]()} + return loggerCache{m: xsync.NewMap[string, logger.Logger]()} } func (c loggerCache) Get(info psrpc.RPCInfo, l logger.Logger) logger.Logger { - wl, _ := c.m.LoadOrCompute(info.Method, func() logger.Logger { - return l.WithComponent("psrpc").WithComponent(info.Service).WithComponent(info.Method) + wl, _ := c.m.LoadOrCompute(info.Method, func() (logger.Logger, bool) { + return l.WithComponent("psrpc").WithComponent(info.Service).WithComponent(info.Method), false }) return wl } From 658576c8683edf36d2fab3fc7a17a5a2152b2d44 Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Thu, 4 Jun 2026 10:35:23 -0700 Subject: [PATCH 02/12] webhook: bump test secret to satisfy go-jose v4 32-byte HS256 minimum --- webhook/webhook_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webhook/webhook_test.go b/webhook/webhook_test.go index bf225bc18..48daa80fb 100644 --- a/webhook/webhook_test.go +++ b/webhook/webhook_test.go @@ -33,7 +33,7 @@ import ( const ( testAPIKey = "mykey" - testAPISecret = "mysecret" + testAPISecret = "mysecret-must-be-at-least-32-bytes" testAddr = ":8765" testUrl = "http://localhost:8765" webhookCheckInterval = 100 * time.Millisecond From 878cf9bb3aad5771c8a377fa1b0703cc1fe71ec8 Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Thu, 4 Jun 2026 10:53:44 -0700 Subject: [PATCH 03/12] deps: switch from go-jose/v4 to golang-jwt/jwt/v5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit go-jose v4 enforces RFC 7518's HMAC key-size minimum (HS256 requires keys ≥ 32 bytes), which would break OSS deployments using short secrets — including the livekit-server --dev default ("devkey":"secret"). golang-jwt/jwt/v5 does not enforce a minimum, preserving the previous behavior. The new combined tokenClaims struct embeds jwt.RegisteredClaims with ClaimGrants so the on-wire payload is unchanged. A token leeway of one minute is set to match go-jose's previous DefaultLeeway. Verify now re-parses the raw token string (golang-jwt does not separate parsing from signature verification), and its first return type changes from *jose.Claims to *jwt.RegisteredClaims; all in-tree callers ignore that return. Reverts the test secret bumps from the previous commit since the length requirement no longer applies. --- auth/accesstoken.go | 33 +++++++++++++---------- auth/accesstoken_test.go | 40 ++++++++++------------------ auth/verifier.go | 56 +++++++++++++++++++++------------------- auth/verifier_test.go | 23 ++++++----------- go.mod | 2 +- go.sum | 4 +-- webhook/webhook_test.go | 2 +- 7 files changed, 74 insertions(+), 86 deletions(-) diff --git a/auth/accesstoken.go b/auth/accesstoken.go index 641ee0505..d483bf821 100644 --- a/auth/accesstoken.go +++ b/auth/accesstoken.go @@ -17,12 +17,18 @@ package auth import ( "time" - "github.com/go-jose/go-jose/v4" - "github.com/go-jose/go-jose/v4/jwt" + "github.com/golang-jwt/jwt/v5" "github.com/livekit/protocol/livekit" ) +// tokenClaims combines the JWT registered claims with LiveKit grants for +// signing and verification. +type tokenClaims struct { + jwt.RegisteredClaims + ClaimGrants +} + const ( defaultValidDuration = 6 * time.Hour ) @@ -174,22 +180,21 @@ func (t *AccessToken) ToJWT() (string, error) { } } - sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: []byte(t.secret)}, - (&jose.SignerOptions{}).WithType("JWT")) - if err != nil { - return "", err - } - validFor := defaultValidDuration if t.validFor > 0 { validFor = t.validFor } - cl := jwt.Claims{ - Issuer: t.apiKey, - NotBefore: jwt.NewNumericDate(time.Now()), - Expiry: jwt.NewNumericDate(time.Now().Add(validFor)), - Subject: t.grant.Identity, + now := time.Now() + claims := tokenClaims{ + RegisteredClaims: jwt.RegisteredClaims{ + Issuer: t.apiKey, + Subject: t.grant.Identity, + IssuedAt: jwt.NewNumericDate(now), + NotBefore: jwt.NewNumericDate(now), + ExpiresAt: jwt.NewNumericDate(now.Add(validFor)), + }, + ClaimGrants: t.grant, } - return jwt.Signed(sig).Claims(cl).Claims(&t.grant).Serialize() + return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(t.secret)) } diff --git a/auth/accesstoken_test.go b/auth/accesstoken_test.go index f0357b5ea..2cad1993d 100644 --- a/auth/accesstoken_test.go +++ b/auth/accesstoken_test.go @@ -21,8 +21,6 @@ import ( "testing" "time" - "github.com/go-jose/go-jose/v4" - "github.com/go-jose/go-jose/v4/jwt" "github.com/stretchr/testify/require" "github.com/livekit/protocol/livekit" @@ -60,12 +58,10 @@ func TestAccessToken(t *testing.T) { require.Len(t, strings.Split(value, "."), 3) // ensure it's a valid JWT - token, err := jwt.ParseSigned(value, []jose.SignatureAlgorithm{jose.HS256}) - require.NoError(t, err) - - decodedGrant := ClaimGrants{} - err = token.UnsafeClaimsWithoutVerification(&decodedGrant) + decoded := tokenClaims{} + _, _, err = unverifiedParser.ParseUnverified(value, &decoded) require.NoError(t, err) + decodedGrant := decoded.ClaimGrants require.EqualValues(t, livekit.ParticipantInfo_AGENT, decodedGrant.GetParticipantKind()) require.EqualValues(t, videoGrant, decodedGrant.Video) @@ -80,15 +76,12 @@ func TestAccessToken(t *testing.T) { SetVideoGrant(&VideoGrant{RoomJoin: true, Room: "myroom"}). ToJWT() require.NoError(t, err) - token, err := jwt.ParseSigned(value, []jose.SignatureAlgorithm{jose.HS256}) - require.NoError(t, err) - - decodedGrant := ClaimGrants{} - err = token.UnsafeClaimsWithoutVerification(&decodedGrant) + decoded := tokenClaims{} + _, _, err = unverifiedParser.ParseUnverified(value, &decoded) require.NoError(t, err) // default validity - require.EqualValues(t, livekit.ParticipantInfo_STANDARD, decodedGrant.GetParticipantKind()) + require.EqualValues(t, livekit.ParticipantInfo_STANDARD, decoded.GetParticipantKind()) }) t.Run("default validity should be more than a minute", func(t *testing.T) { @@ -98,17 +91,13 @@ func TestAccessToken(t *testing.T) { SetVideoGrant(videoGrant) value, err := at.ToJWT() require.NoError(t, err) - token, err := jwt.ParseSigned(value, []jose.SignatureAlgorithm{jose.HS256}) + decoded := tokenClaims{} + _, _, err = unverifiedParser.ParseUnverified(value, &decoded) require.NoError(t, err) - - claim := jwt.Claims{} - decodedGrant := ClaimGrants{} - err = token.UnsafeClaimsWithoutVerification(&claim, &decodedGrant) - require.NoError(t, err) - require.EqualValues(t, videoGrant, decodedGrant.Video) + require.EqualValues(t, videoGrant, decoded.Video) // default validity - require.True(t, claim.Expiry.Time().Sub(claim.IssuedAt.Time()) > time.Minute) + require.True(t, decoded.ExpiresAt.Sub(decoded.IssuedAt.Time) > time.Minute) }) t.Run("room configuration serialization and deserialization", func(t *testing.T) { @@ -136,15 +125,12 @@ func TestAccessToken(t *testing.T) { require.NoError(t, err) // Parse and verify the token - token, err := jwt.ParseSigned(value, []jose.SignatureAlgorithm{jose.HS256}) - require.NoError(t, err) - - decodedGrant := ClaimGrants{} - err = token.UnsafeClaimsWithoutVerification(&decodedGrant) + decoded := tokenClaims{} + _, _, err = unverifiedParser.ParseUnverified(value, &decoded) require.NoError(t, err) // Check if the room configuration was correctly serialized and deserialized - roomDecoded := (*livekit.RoomConfiguration)(decodedGrant.RoomConfig) + roomDecoded := (*livekit.RoomConfiguration)(decoded.RoomConfig) require.NotNil(t, roomDecoded) agents := roomDecoded.Agents require.NotNil(t, agents) diff --git a/auth/verifier.go b/auth/verifier.go index 23d0cf8ab..5bc7c3fb9 100644 --- a/auth/verifier.go +++ b/auth/verifier.go @@ -17,37 +17,38 @@ package auth import ( "time" - "github.com/go-jose/go-jose/v4" - "github.com/go-jose/go-jose/v4/jwt" + "github.com/golang-jwt/jwt/v5" ) -var allowedSignatureAlgorithms = []jose.SignatureAlgorithm{jose.HS256} +// matches go-jose's previous DefaultLeeway for validation tolerance. +const tokenLeeway = time.Minute + +var ( + allowedSigningMethods = []string{jwt.SigningMethodHS256.Alg()} + unverifiedParser = jwt.NewParser(jwt.WithValidMethods(allowedSigningMethods)) +) type APIKeyTokenVerifier struct { - token *jwt.JSONWebToken + raw string identity string apiKey string } -// ParseAPIToken parses an encoded JWT token and +// ParseAPIToken parses an encoded JWT token without verifying its signature. +// The signature is verified later by Verify, which requires the API secret. func ParseAPIToken(raw string) (*APIKeyTokenVerifier, error) { - tok, err := jwt.ParseSigned(raw, allowedSignatureAlgorithms) - if err != nil { - return nil, err - } - - out := jwt.Claims{} - if err := tok.UnsafeClaimsWithoutVerification(&out); err != nil { + claims := &jwt.RegisteredClaims{} + if _, _, err := unverifiedParser.ParseUnverified(raw, claims); err != nil { return nil, err } v := &APIKeyTokenVerifier{ - token: tok, - apiKey: out.Issuer, - identity: out.Subject, + raw: raw, + apiKey: claims.Issuer, + identity: claims.Subject, } if v.identity == "" { - v.identity = out.ID + v.identity = claims.ID } return v, nil } @@ -61,23 +62,26 @@ func (v *APIKeyTokenVerifier) Identity() string { return v.identity } -func (v *APIKeyTokenVerifier) Verify(key interface{}) (*jwt.Claims, *ClaimGrants, error) { +func (v *APIKeyTokenVerifier) Verify(key interface{}) (*jwt.RegisteredClaims, *ClaimGrants, error) { if key == nil || key == "" { return nil, nil, ErrKeysMissing } if s, ok := key.(string); ok { key = []byte(s) } - out := jwt.Claims{} - claims := ClaimGrants{} - if err := v.token.Claims(key, &out, &claims); err != nil { - return nil, nil, err - } - if err := out.Validate(jwt.Expected{Issuer: v.apiKey, Time: time.Now()}); err != nil { + + claims := &tokenClaims{} + _, err := jwt.ParseWithClaims(v.raw, claims, func(*jwt.Token) (interface{}, error) { + return key, nil + }, + jwt.WithValidMethods(allowedSigningMethods), + jwt.WithIssuer(v.apiKey), + jwt.WithLeeway(tokenLeeway), + ) + if err != nil { return nil, nil, err } - // copy over identity - claims.Identity = v.identity - return &out, &claims, nil + claims.ClaimGrants.Identity = v.identity + return &claims.RegisteredClaims, &claims.ClaimGrants, nil } diff --git a/auth/verifier_test.go b/auth/verifier_test.go index 123314bd7..cefc00613 100644 --- a/auth/verifier_test.go +++ b/auth/verifier_test.go @@ -15,12 +15,11 @@ package auth_test import ( + "encoding/json" "testing" "time" - "github.com/go-jose/go-jose/v4" - "github.com/go-jose/go-jose/v4/json" - "github.com/go-jose/go-jose/v4/jwt" + "github.com/golang-jwt/jwt/v5" "github.com/stretchr/testify/require" "github.com/livekit/protocol/auth" @@ -28,7 +27,7 @@ import ( func TestVerifier(t *testing.T) { apiKey := "APID3B67uxk4Nj2GKiRPibAZ9" - secret := "7uxiM/OjTlB+X4Ep6TrZfi+PK2A/ekuPwiw" + secret := "YHC-CUhbQhGeVCaYgn1BNA++" accessToken := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MDg5MzAzMDgsImlzcyI6IkFQSUQzQjY3dXhrNE5qMkdLaVJQaWJBWjkiLCJuYmYiOjE2MDg5MjY3MDgsInJvb21fam9pbiI6dHJ1ZSwicm9vbV9zaWQiOiJteWlkIiwic3ViIjoiQVBJRDNCNjd1eGs0TmoyR0tpUlBpYkFaOSJ9.cmHEBq0MLyRqphmVLM2cLXg5ao5Sro7am8yXhcYKcwE" t.Run("cannot decode with incorrect key", func(t *testing.T) { v, err := auth.ParseAPIToken(accessToken) @@ -102,32 +101,26 @@ func TestVerifier(t *testing.T) { // this server does not yet know about. The server should still accept the // token rather than failing with `unknown field`. This guards against // requiring server upgrades before client upgrades can roll out. - sig, err := jose.NewSigner( - jose.SigningKey{Algorithm: jose.HS256, Key: []byte(secret)}, - (&jose.SignerOptions{}).WithType("JWT"), - ) - require.NoError(t, err) - - claims := map[string]interface{}{ + claims := jwt.MapClaims{ "iss": apiKey, "sub": "me", "nbf": jwt.NewNumericDate(time.Now()), "exp": jwt.NewNumericDate(time.Now().Add(time.Minute)), // unknown top-level claim grants field - "someFutureGrant": map[string]interface{}{"enabled": true}, - "video": map[string]interface{}{ + "someFutureGrant": map[string]any{"enabled": true}, + "video": map[string]any{ "roomJoin": true, "room": "myroom", // unknown field inside a known grant "someFutureVideoField": "future-value", }, - "roomConfig": map[string]interface{}{ + "roomConfig": map[string]any{ "name": "myroom", // unknown field inside a protojson-decoded message "someFutureRoomConfigField": "future-value", }, } - token, err := jwt.Signed(sig).Claims(claims).Serialize() + token, err := jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(secret)) require.NoError(t, err) v, err := auth.ParseAPIToken(token) diff --git a/go.mod b/go.mod index b51df89eb..6aa4d6265 100644 --- a/go.mod +++ b/go.mod @@ -9,8 +9,8 @@ require ( github.com/frostbyte73/core v0.1.1 github.com/fsnotify/fsnotify v1.10.1 github.com/gammazero/deque v1.2.1 - github.com/go-jose/go-jose/v4 v4.1.4 github.com/go-logr/logr v1.4.3 + github.com/golang-jwt/jwt/v5 v5.3.1 github.com/hashicorp/go-retryablehttp v0.7.7 github.com/jxskiss/base62 v1.1.0 github.com/lithammer/shortuuid/v4 v4.2.0 diff --git a/go.sum b/go.sum index 267f5e595..ef8ea6471 100644 --- a/go.sum +++ b/go.sum @@ -54,8 +54,6 @@ github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx5 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/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= -github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -63,6 +61,8 @@ 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/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= +github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= 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.28.1 h1:YWIwi77J4xIsYUwAF/iIuS6haffzIHS8yWI8glSbLWM= diff --git a/webhook/webhook_test.go b/webhook/webhook_test.go index 48daa80fb..bf225bc18 100644 --- a/webhook/webhook_test.go +++ b/webhook/webhook_test.go @@ -33,7 +33,7 @@ import ( const ( testAPIKey = "mykey" - testAPISecret = "mysecret-must-be-at-least-32-bytes" + testAPISecret = "mysecret" testAddr = ":8765" testUrl = "http://localhost:8765" webhookCheckInterval = 100 * time.Millisecond From b6c88bd4c2773e9e1e0c2bb4771bb2fcc237eb02 Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Thu, 4 Jun 2026 11:35:04 -0700 Subject: [PATCH 04/12] deps: drop pkg/errors and go.uber.org/atomic for stdlib pkg/errors was unmaintained since 2021 and stdlib errors + fmt.Errorf %w covers our (wrap-only) usage. Removed from redis/redis.go and observability/egressobs/egress.go. go.uber.org/atomic moves to sync/atomic, which gained generic typed values in Go 1.19+. Most call sites are straightforward import swaps plus s/.Inc()/.Add(1)/. atomic.NewUint64(0) becomes var ... atomic.Uint64. Three stdlib gaps required local wrappers: - configutil.AtomicFloat32/Float64 (Uint32/Uint64 + math.Floatbits) - configutil.AtomicDuration (Int64) - configutil.AtomicString, AtomicTime (atomic.Pointer) - utils/hwstats/cpu.go uses a private atomicFloat64 with the same trick go.uber.org/atomic remains an indirect dep via backend-common; protocol no longer pulls it directly. This is a breaking change for callers of configutil.NewAtomic* helpers that named the returned type explicitly: uber.org/atomic.Bool returns become sync/atomic.Bool, and Float32/Float64/String/Duration/Time returns become *configutil.Atomic wrappers. --- go.mod | 3 +- logger/pionlogger/zaplogadapter.go | 2 +- logger/zaputil/deferrer.go | 2 +- logger/zaputil/zaputil_test.go | 4 +- observability/egressobs/egress.go | 17 +++--- redis/redis.go | 5 +- rpc/metrics.go | 2 +- utils/configobserver.go | 2 +- utils/configutil/atomic.go | 98 ++++++++++++++++++++++++------ utils/hedge_test.go | 2 +- utils/hwstats/cpu.go | 14 ++++- utils/multitonservice_test.go | 4 +- utils/parallel.go | 5 +- utils/rate.go | 2 +- utils/rate_test.go | 6 +- webhook/url_notifier.go | 4 +- webhook/webhook_test.go | 47 +++++++------- 17 files changed, 145 insertions(+), 74 deletions(-) diff --git a/go.mod b/go.mod index 6aa4d6265..46a3cf584 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,6 @@ require ( github.com/pion/logging v0.2.4 github.com/pion/sdp/v3 v3.0.18 github.com/pion/webrtc/v4 v4.2.14 - github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.22.0 github.com/prometheus/procfs v0.16.1 github.com/puzpuzpuz/xsync/v4 v4.5.0 @@ -34,7 +33,6 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 go.opentelemetry.io/otel/sdk v1.43.0 go.opentelemetry.io/otel/trace v1.44.0 - go.uber.org/atomic v1.11.0 go.uber.org/multierr v1.11.0 go.uber.org/zap v1.28.0 go.uber.org/zap/exp v0.3.0 @@ -89,6 +87,7 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect go.opentelemetry.io/otel/metric v1.44.0 // indirect go.opentelemetry.io/proto/otlp v1.10.0 // indirect + go.uber.org/atomic v1.11.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.52.0 // indirect golang.org/x/net v0.55.0 // indirect diff --git a/logger/pionlogger/zaplogadapter.go b/logger/pionlogger/zaplogadapter.go index 07ad22fa8..db57092e8 100644 --- a/logger/pionlogger/zaplogadapter.go +++ b/logger/pionlogger/zaplogadapter.go @@ -17,9 +17,9 @@ package pionlogger import ( "fmt" - "go.uber.org/atomic" "go.uber.org/zap" "go.uber.org/zap/zapcore" + "sync/atomic" "github.com/livekit/protocol/logger" ) diff --git a/logger/zaputil/deferrer.go b/logger/zaputil/deferrer.go index 57ea5a84c..4b19d163b 100644 --- a/logger/zaputil/deferrer.go +++ b/logger/zaputil/deferrer.go @@ -18,9 +18,9 @@ import ( "slices" "sync" - "go.uber.org/atomic" "go.uber.org/zap" "go.uber.org/zap/zapcore" + "sync/atomic" ) type deferredWrite struct { diff --git a/logger/zaputil/zaputil_test.go b/logger/zaputil/zaputil_test.go index bbbb6c511..f9729c682 100644 --- a/logger/zaputil/zaputil_test.go +++ b/logger/zaputil/zaputil_test.go @@ -15,8 +15,8 @@ package zaputil import ( - "go.uber.org/atomic" "go.uber.org/zap/zapcore" + "sync/atomic" ) type testStringer string @@ -53,6 +53,6 @@ func (c *testCore) With(fields []zapcore.Field) zapcore.Core { func (c *testCore) Write(entry zapcore.Entry, fields []zapcore.Field) error { c.init() - c.writeCount.Inc() + c.writeCount.Add(1) return nil } diff --git a/observability/egressobs/egress.go b/observability/egressobs/egress.go index 3a1b5872f..d72fd48ff 100644 --- a/observability/egressobs/egress.go +++ b/observability/egressobs/egress.go @@ -2,11 +2,10 @@ package egressobs import ( "encoding/json" + "fmt" "github.com/livekit/protocol/utils/protojson" - "github.com/pkg/errors" - "github.com/livekit/protocol/egress" "github.com/livekit/protocol/livekit" ) @@ -115,37 +114,37 @@ func GetRequest(info *livekit.EgressInfo) (string, error) { case *livekit.EgressInfo_Replay: b, err := protojson.Marshal(req.Replay) if err != nil { - return "", errors.Wrap(err, "failed serializing Replay request") + return "", fmt.Errorf("failed serializing Replay request: %w", err) } return string(b), nil case *livekit.EgressInfo_RoomComposite: b, err := protojson.Marshal(req.RoomComposite) if err != nil { - return "", errors.Wrap(err, "failed serializing RoomComposite request") + return "", fmt.Errorf("failed serializing RoomComposite request: %w", err) } return string(b), nil case *livekit.EgressInfo_Web: b, err := protojson.Marshal(req.Web) if err != nil { - return "", errors.Wrap(err, "failed serializing Web request") + return "", fmt.Errorf("failed serializing Web request: %w", err) } return string(b), nil case *livekit.EgressInfo_Participant: b, err := protojson.Marshal(req.Participant) if err != nil { - return "", errors.Wrap(err, "failed serializing Participant request") + return "", fmt.Errorf("failed serializing Participant request: %w", err) } return string(b), nil case *livekit.EgressInfo_TrackComposite: b, err := protojson.Marshal(req.TrackComposite) if err != nil { - return "", errors.Wrap(err, "failed serializing TrackComposite request") + return "", fmt.Errorf("failed serializing TrackComposite request: %w", err) } return string(b), nil case *livekit.EgressInfo_Track: b, err := protojson.Marshal(req.Track) if err != nil { - return "", errors.Wrap(err, "failed serializing Track request") + return "", fmt.Errorf("failed serializing Track request: %w", err) } return string(b), nil default: @@ -181,7 +180,7 @@ func GetResult(info *livekit.EgressInfo) (string, error) { } b, err := json.Marshal(results) if err != nil { - return "", errors.Wrap(err, "failed serializing results") + return "", fmt.Errorf("failed serializing results: %w", err) } return string(b), nil } diff --git a/redis/redis.go b/redis/redis.go index 5e2648a88..f98fccc40 100644 --- a/redis/redis.go +++ b/redis/redis.go @@ -17,9 +17,10 @@ package redis import ( "context" "crypto/tls" + "errors" + "fmt" "time" - "github.com/pkg/errors" "github.com/redis/go-redis/v9" "github.com/livekit/protocol/xtls" @@ -155,7 +156,7 @@ func GetRedisClient(conf *RedisConfig) (redis.UniversalClient, error) { rc = redis.NewUniversalClient(rcOptions) if err := rc.Ping(context.Background()).Err(); err != nil { - err = errors.Wrap(err, "unable to connect to redis") + err = fmt.Errorf("unable to connect to redis: %w", err) return nil, err } diff --git a/rpc/metrics.go b/rpc/metrics.go index c1b966fd4..5775dd842 100644 --- a/rpc/metrics.go +++ b/rpc/metrics.go @@ -20,8 +20,8 @@ import ( "time" "github.com/prometheus/client_golang/prometheus" - "go.uber.org/atomic" "golang.org/x/exp/maps" + "sync/atomic" "github.com/livekit/psrpc" "github.com/livekit/psrpc/pkg/middleware" diff --git a/utils/configobserver.go b/utils/configobserver.go index c05c0e75e..e9cb803b9 100644 --- a/utils/configobserver.go +++ b/utils/configobserver.go @@ -19,8 +19,8 @@ import ( "os" "github.com/fsnotify/fsnotify" - "go.uber.org/atomic" "gopkg.in/yaml.v3" + "sync/atomic" "github.com/livekit/protocol/logger" "github.com/livekit/protocol/utils/events" diff --git a/utils/configutil/atomic.go b/utils/configutil/atomic.go index 2725097fb..69c6e034a 100644 --- a/utils/configutil/atomic.go +++ b/utils/configutil/atomic.go @@ -15,9 +15,9 @@ package configutil import ( + "math" + "sync/atomic" "time" - - "go.uber.org/atomic" ) type ConfigObserver[T any] interface { @@ -25,48 +25,110 @@ type ConfigObserver[T any] interface { Load() *T } +// AtomicFloat32 stores a float32 in a sync/atomic.Uint32 via its IEEE-754 bit +// representation. Exists because sync/atomic has no Float32. +type AtomicFloat32 struct { + v atomic.Uint32 +} + +func (a *AtomicFloat32) Load() float32 { return math.Float32frombits(a.v.Load()) } +func (a *AtomicFloat32) Store(v float32) { a.v.Store(math.Float32bits(v)) } +func (a *AtomicFloat32) Swap(v float32) float32 { + return math.Float32frombits(a.v.Swap(math.Float32bits(v))) +} + +// AtomicFloat64 stores a float64 in a sync/atomic.Uint64 via its IEEE-754 bit +// representation. Exists because sync/atomic has no Float64. +type AtomicFloat64 struct { + v atomic.Uint64 +} + +func (a *AtomicFloat64) Load() float64 { return math.Float64frombits(a.v.Load()) } +func (a *AtomicFloat64) Store(v float64) { a.v.Store(math.Float64bits(v)) } +func (a *AtomicFloat64) Swap(v float64) float64 { + return math.Float64frombits(a.v.Swap(math.Float64bits(v))) +} + +// AtomicDuration stores a time.Duration in a sync/atomic.Int64 (Duration is int64). +type AtomicDuration struct { + v atomic.Int64 +} + +func (a *AtomicDuration) Load() time.Duration { return time.Duration(a.v.Load()) } +func (a *AtomicDuration) Store(v time.Duration) { a.v.Store(int64(v)) } +func (a *AtomicDuration) Swap(v time.Duration) time.Duration { + return time.Duration(a.v.Swap(int64(v))) +} + +// AtomicString stores a string atomically. Backed by atomic.Pointer to a copy, +// since strings can vary in length. +type AtomicString struct { + v atomic.Pointer[string] +} + +func (a *AtomicString) Load() string { + if p := a.v.Load(); p != nil { + return *p + } + return "" +} +func (a *AtomicString) Store(v string) { a.v.Store(&v) } + +// AtomicTime stores a time.Time atomically via atomic.Pointer. +type AtomicTime struct { + v atomic.Pointer[time.Time] +} + +func (a *AtomicTime) Load() time.Time { + if p := a.v.Load(); p != nil { + return *p + } + return time.Time{} +} +func (a *AtomicTime) Store(v time.Time) { a.v.Store(&v) } + func NewAtomicBool[C any](config ConfigObserver[C], accessor func(*C) bool) *atomic.Bool { - return newAtomic(atomic.NewBool, config, accessor) + return newAtomic(func(v bool) *atomic.Bool { a := &atomic.Bool{}; a.Store(v); return a }, config, accessor) } -func NewAtomicDuration[C any](config ConfigObserver[C], accessor func(*C) time.Duration) *atomic.Duration { - return newAtomic(atomic.NewDuration, config, accessor) +func NewAtomicDuration[C any](config ConfigObserver[C], accessor func(*C) time.Duration) *AtomicDuration { + return newAtomic(func(v time.Duration) *AtomicDuration { a := &AtomicDuration{}; a.Store(v); return a }, config, accessor) } -func NewAtomicFloat32[C any](config ConfigObserver[C], accessor func(*C) float32) *atomic.Float32 { - return newAtomic(atomic.NewFloat32, config, accessor) +func NewAtomicFloat32[C any](config ConfigObserver[C], accessor func(*C) float32) *AtomicFloat32 { + return newAtomic(func(v float32) *AtomicFloat32 { a := &AtomicFloat32{}; a.Store(v); return a }, config, accessor) } -func NewAtomicFloat64[C any](config ConfigObserver[C], accessor func(*C) float64) *atomic.Float64 { - return newAtomic(atomic.NewFloat64, config, accessor) +func NewAtomicFloat64[C any](config ConfigObserver[C], accessor func(*C) float64) *AtomicFloat64 { + return newAtomic(func(v float64) *AtomicFloat64 { a := &AtomicFloat64{}; a.Store(v); return a }, config, accessor) } func NewAtomicInt32[C any](config ConfigObserver[C], accessor func(*C) int32) *atomic.Int32 { - return newAtomic(atomic.NewInt32, config, accessor) + return newAtomic(func(v int32) *atomic.Int32 { a := &atomic.Int32{}; a.Store(v); return a }, config, accessor) } func NewAtomicInt64[C any](config ConfigObserver[C], accessor func(*C) int64) *atomic.Int64 { - return newAtomic(atomic.NewInt64, config, accessor) + return newAtomic(func(v int64) *atomic.Int64 { a := &atomic.Int64{}; a.Store(v); return a }, config, accessor) } -func NewAtomicString[C any](config ConfigObserver[C], accessor func(*C) string) *atomic.String { - return newAtomic(atomic.NewString, config, accessor) +func NewAtomicString[C any](config ConfigObserver[C], accessor func(*C) string) *AtomicString { + return newAtomic(func(v string) *AtomicString { a := &AtomicString{}; a.Store(v); return a }, config, accessor) } -func NewAtomicTime[C any](config ConfigObserver[C], accessor func(*C) time.Time) *atomic.Time { - return newAtomic(atomic.NewTime, config, accessor) +func NewAtomicTime[C any](config ConfigObserver[C], accessor func(*C) time.Time) *AtomicTime { + return newAtomic(func(v time.Time) *AtomicTime { a := &AtomicTime{}; a.Store(v); return a }, config, accessor) } func NewAtomicUint32[C any](config ConfigObserver[C], accessor func(*C) uint32) *atomic.Uint32 { - return newAtomic(atomic.NewUint32, config, accessor) + return newAtomic(func(v uint32) *atomic.Uint32 { a := &atomic.Uint32{}; a.Store(v); return a }, config, accessor) } func NewAtomicUint64[C any](config ConfigObserver[C], accessor func(*C) uint64) *atomic.Uint64 { - return newAtomic(atomic.NewUint64, config, accessor) + return newAtomic(func(v uint64) *atomic.Uint64 { a := &atomic.Uint64{}; a.Store(v); return a }, config, accessor) } func NewAtomicPointer[T, C any](config ConfigObserver[C], accessor func(*C) *T) *atomic.Pointer[T] { - return newAtomic(atomic.NewPointer[T], config, accessor) + return newAtomic(func(v *T) *atomic.Pointer[T] { a := &atomic.Pointer[T]{}; a.Store(v); return a }, config, accessor) } type AtomicValue[T any] struct { diff --git a/utils/hedge_test.go b/utils/hedge_test.go index fdf86d41c..2728a8ad0 100644 --- a/utils/hedge_test.go +++ b/utils/hedge_test.go @@ -21,7 +21,7 @@ import ( "time" "github.com/stretchr/testify/require" - "go.uber.org/atomic" + "sync/atomic" ) func TestHedgeCall(t *testing.T) { diff --git a/utils/hwstats/cpu.go b/utils/hwstats/cpu.go index 97e9ab700..89bedf7bf 100644 --- a/utils/hwstats/cpu.go +++ b/utils/hwstats/cpu.go @@ -15,18 +15,28 @@ package hwstats import ( + "math" "os" "strconv" "sync" + "sync/atomic" "time" "github.com/frostbyte73/core" "github.com/prometheus/procfs" - "go.uber.org/atomic" "github.com/livekit/protocol/logger" ) +// atomicFloat64 stores a float64 in a sync/atomic.Uint64 via its IEEE-754 bit +// representation. sync/atomic has no Float64. +type atomicFloat64 struct { + v atomic.Uint64 +} + +func (a *atomicFloat64) Load() float64 { return math.Float64frombits(a.v.Load()) } +func (a *atomicFloat64) Store(v float64) { a.v.Store(math.Float64bits(v)) } + // This object returns cgroup quota aware cpu stats. On other systems than Linux, // it falls back to full system stats @@ -63,7 +73,7 @@ type platformCPUMonitor interface { } type CPUStats struct { - idleCPUs atomic.Float64 + idleCPUs atomicFloat64 platform platformCPUMonitor idleCallback func(idle float64) diff --git a/utils/multitonservice_test.go b/utils/multitonservice_test.go index 263f57c0f..a02d23796 100644 --- a/utils/multitonservice_test.go +++ b/utils/multitonservice_test.go @@ -19,7 +19,7 @@ import ( "time" "github.com/stretchr/testify/require" - "go.uber.org/atomic" + "sync/atomic" ) type testService struct { @@ -31,7 +31,7 @@ func newTestService(key string) *testService { return &testService{key: key} } -func (s *testService) Kill() { s.killCalls.Inc() } +func (s *testService) Kill() { s.killCalls.Add(1) } func TestMultitonService(t *testing.T) { t.Run("start and stop are called", func(t *testing.T) { diff --git a/utils/parallel.go b/utils/parallel.go index 1593ed9ff..caa4135ee 100644 --- a/utils/parallel.go +++ b/utils/parallel.go @@ -17,8 +17,7 @@ package utils import ( "runtime" "sync" - - "go.uber.org/atomic" + "sync/atomic" ) // ParallelExec will executes the given function with each element of vals, if len(vals) >= parallelThreshold, @@ -32,7 +31,7 @@ func ParallelExec[T any](vals []T, parallelThreshold, step uint64, fn func(T)) { } // parallel - enables much more efficient multi-core utilization - start := atomic.NewUint64(0) + var start atomic.Uint64 end := uint64(len(vals)) var wg sync.WaitGroup diff --git a/utils/rate.go b/utils/rate.go index 266dced7e..81149ec9c 100644 --- a/utils/rate.go +++ b/utils/rate.go @@ -27,7 +27,7 @@ import ( "sync" "time" - "go.uber.org/atomic" + "sync/atomic" ) type LeakyBucket struct { diff --git a/utils/rate_test.go b/utils/rate_test.go index c523a37ac..79225590f 100644 --- a/utils/rate_test.go +++ b/utils/rate_test.go @@ -29,7 +29,7 @@ import ( "testing" "time" - "go.uber.org/atomic" + "sync/atomic" "github.com/benbjohnson/clock" "github.com/stretchr/testify/require" @@ -250,7 +250,7 @@ func (r *runnerImpl) startTaking(rls ...Limiter) { for _, rl := range rls { rl.Take() } - r.count.Inc() + r.count.Add(1) select { case <-r.doneCh: return @@ -265,7 +265,7 @@ func (r *runnerImpl) takeOnceAfter(d time.Duration, rl Limiter) { r.wg.Add(1) r.afterFunc(d, func() { rl.Take() - r.count.Inc() + r.count.Add(1) r.wg.Done() }) } diff --git a/webhook/url_notifier.go b/webhook/url_notifier.go index ee96c376f..1c97c1839 100644 --- a/webhook/url_notifier.go +++ b/webhook/url_notifier.go @@ -27,7 +27,7 @@ import ( "github.com/frostbyte73/core" "github.com/hashicorp/go-retryablehttp" - "go.uber.org/atomic" + "sync/atomic" "github.com/livekit/protocol/auth" "github.com/livekit/protocol/livekit" @@ -224,7 +224,7 @@ func (n *URLNotifier) QueueNotify(ctx context.Context, event *livekit.WebhookEve ph(ctx, whi) } }) { - n.dropped.Inc() + n.dropped.Add(1) fields := logFields(event, params.URL) params.Logger.Infow("dropped webhook", fields...) diff --git a/webhook/webhook_test.go b/webhook/webhook_test.go index bf225bc18..e5e97ab26 100644 --- a/webhook/webhook_test.go +++ b/webhook/webhook_test.go @@ -23,9 +23,10 @@ import ( "testing" "time" + "sync/atomic" + "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/require" - "go.uber.org/atomic" "github.com/livekit/protocol/auth" "github.com/livekit/protocol/livekit" @@ -100,7 +101,7 @@ func TestURLNotifierDropped(t *testing.T) { s.handler = func(w http.ResponseWriter, r *http.Request) { decodedEvent, err := ReceiveWebhookEvent(r, authProvider) require.NoError(t, err) - totalReceived.Inc() + totalReceived.Add(1) totalDropped.Add(decodedEvent.NumDropped) } // send multiple notifications @@ -133,7 +134,7 @@ func TestURLNotifierLifecycle(t *testing.T) { urlNotifier := newTestNotifier() numCalled := atomic.Int32{} s.handler = func(w http.ResponseWriter, r *http.Request) { - numCalled.Inc() + numCalled.Add(1) } for i := 0; i < 10; i++ { _ = urlNotifier.QueueNotify(context.Background(), &livekit.WebhookEvent{Event: EventRoomStarted}) @@ -147,7 +148,7 @@ func TestURLNotifierLifecycle(t *testing.T) { urlNotifier := newTestNotifier() numCalled := atomic.Int32{} s.handler = func(w http.ResponseWriter, r *http.Request) { - numCalled.Inc() + numCalled.Add(1) } for i := 0; i < 10; i++ { _ = urlNotifier.QueueNotify(context.Background(), &livekit.WebhookEvent{Event: EventRoomStarted}) @@ -182,7 +183,7 @@ func TestURLNotifierLifecycle(t *testing.T) { time.Sleep(time.Second) if r.Context().Err() == nil { // inc if not canceled - numCalled.Inc() + numCalled.Add(1) } } defer urlNotifier.Stop(false) @@ -237,7 +238,7 @@ func TestURLNotifierFilter(t *testing.T) { numCalled := atomic.Int32{} s.handler = func(w http.ResponseWriter, r *http.Request) { - numCalled.Inc() + numCalled.Add(1) } _ = urlNotifier.QueueNotify(context.Background(), &livekit.WebhookEvent{Event: EventRoomStarted}) @@ -268,7 +269,7 @@ func TestURLNotifierFilter(t *testing.T) { numCalled := atomic.Int32{} s.handler = func(w http.ResponseWriter, r *http.Request) { - numCalled.Inc() + numCalled.Add(1) } _ = urlNotifier.QueueNotify(context.Background(), &livekit.WebhookEvent{Event: EventRoomStarted}) @@ -299,7 +300,7 @@ func TestURLNotifierFilter(t *testing.T) { numCalled := atomic.Int32{} s.handler = func(w http.ResponseWriter, r *http.Request) { - numCalled.Inc() + numCalled.Add(1) } _ = urlNotifier.QueueNotify(context.Background(), &livekit.WebhookEvent{Event: EventRoomStarted}) @@ -331,7 +332,7 @@ func TestURLNotifierFilter(t *testing.T) { numCalled := atomic.Int32{} s.handler = func(w http.ResponseWriter, r *http.Request) { - numCalled.Inc() + numCalled.Add(1) } // EventRoomStarted should be allowed as IncludeEvents take precedence @@ -415,21 +416,21 @@ func TestResourceURLNotifierDropped(t *testing.T) { s.handler = func(w http.ResponseWriter, r *http.Request) { _, err := ReceiveWebhookEvent(r, authProvider) require.NoError(t, err) - totalReceived.Inc() + totalReceived.Add(1) } // send multiple notifications for i := 0; i < 10; i++ { err := resourceURLNotifier.QueueNotify(context.Background(), &livekit.WebhookEvent{Event: EventRoomStarted}) if err == errQueueFull { - totalDropped.Inc() + totalDropped.Add(1) } err = resourceURLNotifier.QueueNotify(context.Background(), &livekit.WebhookEvent{Event: EventParticipantJoined}) if err == errQueueFull { - totalDropped.Inc() + totalDropped.Add(1) } err = resourceURLNotifier.QueueNotify(context.Background(), &livekit.WebhookEvent{Event: EventRoomFinished}) if err == errQueueFull { - totalDropped.Inc() + totalDropped.Add(1) } } @@ -456,7 +457,7 @@ func TestResourceURLNotifierDropped(t *testing.T) { time.Sleep(5 * time.Millisecond) _, err := ReceiveWebhookEvent(r, authProvider) require.NoError(t, err) - totalReceived.Inc() + totalReceived.Add(1) } // send multiple notifications for i := 0; i < 10; i++ { @@ -482,7 +483,7 @@ func TestResourceURLNotifierDropped(t *testing.T) { s.handler = func(w http.ResponseWriter, r *http.Request) { _, err := ReceiveWebhookEvent(r, authProvider) require.NoError(t, err) - totalReceived.Inc() + totalReceived.Add(1) } // check that resource queues change for the same event key @@ -566,7 +567,7 @@ func TestResourceURLNotifierLifecycle(t *testing.T) { resourceURLNotifier := newTestResourceNotifier(200*time.Millisecond, 200*time.Millisecond, 50) numCalled := atomic.Int32{} s.handler = func(w http.ResponseWriter, r *http.Request) { - numCalled.Inc() + numCalled.Add(1) } for i := 0; i < 10; i++ { roomName := fmt.Sprintf("room%d", i) @@ -609,7 +610,7 @@ func TestResourceURLNotifierLifecycle(t *testing.T) { resourceURLNotifier := newTestResourceNotifier(time.Minute, 200*time.Millisecond, 50) numCalled := atomic.Int32{} s.handler = func(w http.ResponseWriter, r *http.Request) { - numCalled.Inc() + numCalled.Add(1) } for i := 0; i < 10; i++ { _ = resourceURLNotifier.QueueNotify(context.Background(), &livekit.WebhookEvent{Event: EventRoomStarted}) @@ -623,7 +624,7 @@ func TestResourceURLNotifierLifecycle(t *testing.T) { resourceURLNotifier := newTestResourceNotifier(time.Minute, 200*time.Millisecond, 50) numCalled := atomic.Int32{} s.handler = func(w http.ResponseWriter, r *http.Request) { - numCalled.Inc() + numCalled.Add(1) } for i := 0; i < 10; i++ { _ = resourceURLNotifier.QueueNotify(context.Background(), &livekit.WebhookEvent{Event: EventRoomStarted}) @@ -661,7 +662,7 @@ func TestResourceURLNotifierLifecycle(t *testing.T) { time.Sleep(time.Second) if r.Context().Err() == nil { // inc if not canceled - numCalled.Inc() + numCalled.Add(1) } } defer resourceURLNotifier.Stop(false) @@ -720,7 +721,7 @@ func TestResourceURLNotifierFilter(t *testing.T) { numCalled := atomic.Int32{} s.handler = func(w http.ResponseWriter, r *http.Request) { - numCalled.Inc() + numCalled.Add(1) } _ = resourceURLNotifier.QueueNotify(context.Background(), &livekit.WebhookEvent{Event: EventRoomStarted}) @@ -752,7 +753,7 @@ func TestResourceURLNotifierFilter(t *testing.T) { numCalled := atomic.Int32{} s.handler = func(w http.ResponseWriter, r *http.Request) { - numCalled.Inc() + numCalled.Add(1) } _ = resourceURLNotifier.QueueNotify(context.Background(), &livekit.WebhookEvent{Event: EventRoomStarted}) @@ -784,7 +785,7 @@ func TestResourceURLNotifierFilter(t *testing.T) { numCalled := atomic.Int32{} s.handler = func(w http.ResponseWriter, r *http.Request) { - numCalled.Inc() + numCalled.Add(1) } _ = resourceURLNotifier.QueueNotify(context.Background(), &livekit.WebhookEvent{Event: EventRoomStarted}) @@ -817,7 +818,7 @@ func TestResourceURLNotifierFilter(t *testing.T) { numCalled := atomic.Int32{} s.handler = func(w http.ResponseWriter, r *http.Request) { - numCalled.Inc() + numCalled.Add(1) } // EventRoomStarted should be allowed as IncludeEvents take precedence From ab1b54f925cf35fdf07f914720a032c7a42f8481 Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Thu, 4 Jun 2026 13:47:19 -0700 Subject: [PATCH 05/12] deps: revert go.uber.org/atomic migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The stdlib sync/atomic migration was reverted because: - sync/atomic lacks Float32/Float64/String/Duration/Time types, requiring shim types around bit-packing or atomic.Pointer that complicate the configutil public API and don't carry their weight at this scale. - configutil.NewAtomic* return-type changes cascade as breaking changes across all downstream consumers. pkg/errors → stdlib stays. jwt and xsync upgrades stay. --- go.mod | 2 +- logger/pionlogger/zaplogadapter.go | 2 +- logger/zaputil/deferrer.go | 2 +- logger/zaputil/zaputil_test.go | 4 +- rpc/metrics.go | 2 +- utils/configobserver.go | 2 +- utils/configutil/atomic.go | 98 ++++++------------------------ utils/hedge_test.go | 2 +- utils/hwstats/cpu.go | 14 +---- utils/multitonservice_test.go | 4 +- utils/parallel.go | 5 +- utils/rate.go | 2 +- utils/rate_test.go | 6 +- webhook/url_notifier.go | 4 +- webhook/webhook_test.go | 47 +++++++------- 15 files changed, 62 insertions(+), 134 deletions(-) diff --git a/go.mod b/go.mod index 46a3cf584..c8044bc28 100644 --- a/go.mod +++ b/go.mod @@ -33,6 +33,7 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 go.opentelemetry.io/otel/sdk v1.43.0 go.opentelemetry.io/otel/trace v1.44.0 + go.uber.org/atomic v1.11.0 go.uber.org/multierr v1.11.0 go.uber.org/zap v1.28.0 go.uber.org/zap/exp v0.3.0 @@ -87,7 +88,6 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect go.opentelemetry.io/otel/metric v1.44.0 // indirect go.opentelemetry.io/proto/otlp v1.10.0 // indirect - go.uber.org/atomic v1.11.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/crypto v0.52.0 // indirect golang.org/x/net v0.55.0 // indirect diff --git a/logger/pionlogger/zaplogadapter.go b/logger/pionlogger/zaplogadapter.go index db57092e8..07ad22fa8 100644 --- a/logger/pionlogger/zaplogadapter.go +++ b/logger/pionlogger/zaplogadapter.go @@ -17,9 +17,9 @@ package pionlogger import ( "fmt" + "go.uber.org/atomic" "go.uber.org/zap" "go.uber.org/zap/zapcore" - "sync/atomic" "github.com/livekit/protocol/logger" ) diff --git a/logger/zaputil/deferrer.go b/logger/zaputil/deferrer.go index 4b19d163b..57ea5a84c 100644 --- a/logger/zaputil/deferrer.go +++ b/logger/zaputil/deferrer.go @@ -18,9 +18,9 @@ import ( "slices" "sync" + "go.uber.org/atomic" "go.uber.org/zap" "go.uber.org/zap/zapcore" - "sync/atomic" ) type deferredWrite struct { diff --git a/logger/zaputil/zaputil_test.go b/logger/zaputil/zaputil_test.go index f9729c682..bbbb6c511 100644 --- a/logger/zaputil/zaputil_test.go +++ b/logger/zaputil/zaputil_test.go @@ -15,8 +15,8 @@ package zaputil import ( + "go.uber.org/atomic" "go.uber.org/zap/zapcore" - "sync/atomic" ) type testStringer string @@ -53,6 +53,6 @@ func (c *testCore) With(fields []zapcore.Field) zapcore.Core { func (c *testCore) Write(entry zapcore.Entry, fields []zapcore.Field) error { c.init() - c.writeCount.Add(1) + c.writeCount.Inc() return nil } diff --git a/rpc/metrics.go b/rpc/metrics.go index 5775dd842..c1b966fd4 100644 --- a/rpc/metrics.go +++ b/rpc/metrics.go @@ -20,8 +20,8 @@ import ( "time" "github.com/prometheus/client_golang/prometheus" + "go.uber.org/atomic" "golang.org/x/exp/maps" - "sync/atomic" "github.com/livekit/psrpc" "github.com/livekit/psrpc/pkg/middleware" diff --git a/utils/configobserver.go b/utils/configobserver.go index e9cb803b9..c05c0e75e 100644 --- a/utils/configobserver.go +++ b/utils/configobserver.go @@ -19,8 +19,8 @@ import ( "os" "github.com/fsnotify/fsnotify" + "go.uber.org/atomic" "gopkg.in/yaml.v3" - "sync/atomic" "github.com/livekit/protocol/logger" "github.com/livekit/protocol/utils/events" diff --git a/utils/configutil/atomic.go b/utils/configutil/atomic.go index 69c6e034a..2725097fb 100644 --- a/utils/configutil/atomic.go +++ b/utils/configutil/atomic.go @@ -15,9 +15,9 @@ package configutil import ( - "math" - "sync/atomic" "time" + + "go.uber.org/atomic" ) type ConfigObserver[T any] interface { @@ -25,110 +25,48 @@ type ConfigObserver[T any] interface { Load() *T } -// AtomicFloat32 stores a float32 in a sync/atomic.Uint32 via its IEEE-754 bit -// representation. Exists because sync/atomic has no Float32. -type AtomicFloat32 struct { - v atomic.Uint32 -} - -func (a *AtomicFloat32) Load() float32 { return math.Float32frombits(a.v.Load()) } -func (a *AtomicFloat32) Store(v float32) { a.v.Store(math.Float32bits(v)) } -func (a *AtomicFloat32) Swap(v float32) float32 { - return math.Float32frombits(a.v.Swap(math.Float32bits(v))) -} - -// AtomicFloat64 stores a float64 in a sync/atomic.Uint64 via its IEEE-754 bit -// representation. Exists because sync/atomic has no Float64. -type AtomicFloat64 struct { - v atomic.Uint64 -} - -func (a *AtomicFloat64) Load() float64 { return math.Float64frombits(a.v.Load()) } -func (a *AtomicFloat64) Store(v float64) { a.v.Store(math.Float64bits(v)) } -func (a *AtomicFloat64) Swap(v float64) float64 { - return math.Float64frombits(a.v.Swap(math.Float64bits(v))) -} - -// AtomicDuration stores a time.Duration in a sync/atomic.Int64 (Duration is int64). -type AtomicDuration struct { - v atomic.Int64 -} - -func (a *AtomicDuration) Load() time.Duration { return time.Duration(a.v.Load()) } -func (a *AtomicDuration) Store(v time.Duration) { a.v.Store(int64(v)) } -func (a *AtomicDuration) Swap(v time.Duration) time.Duration { - return time.Duration(a.v.Swap(int64(v))) -} - -// AtomicString stores a string atomically. Backed by atomic.Pointer to a copy, -// since strings can vary in length. -type AtomicString struct { - v atomic.Pointer[string] -} - -func (a *AtomicString) Load() string { - if p := a.v.Load(); p != nil { - return *p - } - return "" -} -func (a *AtomicString) Store(v string) { a.v.Store(&v) } - -// AtomicTime stores a time.Time atomically via atomic.Pointer. -type AtomicTime struct { - v atomic.Pointer[time.Time] -} - -func (a *AtomicTime) Load() time.Time { - if p := a.v.Load(); p != nil { - return *p - } - return time.Time{} -} -func (a *AtomicTime) Store(v time.Time) { a.v.Store(&v) } - func NewAtomicBool[C any](config ConfigObserver[C], accessor func(*C) bool) *atomic.Bool { - return newAtomic(func(v bool) *atomic.Bool { a := &atomic.Bool{}; a.Store(v); return a }, config, accessor) + return newAtomic(atomic.NewBool, config, accessor) } -func NewAtomicDuration[C any](config ConfigObserver[C], accessor func(*C) time.Duration) *AtomicDuration { - return newAtomic(func(v time.Duration) *AtomicDuration { a := &AtomicDuration{}; a.Store(v); return a }, config, accessor) +func NewAtomicDuration[C any](config ConfigObserver[C], accessor func(*C) time.Duration) *atomic.Duration { + return newAtomic(atomic.NewDuration, config, accessor) } -func NewAtomicFloat32[C any](config ConfigObserver[C], accessor func(*C) float32) *AtomicFloat32 { - return newAtomic(func(v float32) *AtomicFloat32 { a := &AtomicFloat32{}; a.Store(v); return a }, config, accessor) +func NewAtomicFloat32[C any](config ConfigObserver[C], accessor func(*C) float32) *atomic.Float32 { + return newAtomic(atomic.NewFloat32, config, accessor) } -func NewAtomicFloat64[C any](config ConfigObserver[C], accessor func(*C) float64) *AtomicFloat64 { - return newAtomic(func(v float64) *AtomicFloat64 { a := &AtomicFloat64{}; a.Store(v); return a }, config, accessor) +func NewAtomicFloat64[C any](config ConfigObserver[C], accessor func(*C) float64) *atomic.Float64 { + return newAtomic(atomic.NewFloat64, config, accessor) } func NewAtomicInt32[C any](config ConfigObserver[C], accessor func(*C) int32) *atomic.Int32 { - return newAtomic(func(v int32) *atomic.Int32 { a := &atomic.Int32{}; a.Store(v); return a }, config, accessor) + return newAtomic(atomic.NewInt32, config, accessor) } func NewAtomicInt64[C any](config ConfigObserver[C], accessor func(*C) int64) *atomic.Int64 { - return newAtomic(func(v int64) *atomic.Int64 { a := &atomic.Int64{}; a.Store(v); return a }, config, accessor) + return newAtomic(atomic.NewInt64, config, accessor) } -func NewAtomicString[C any](config ConfigObserver[C], accessor func(*C) string) *AtomicString { - return newAtomic(func(v string) *AtomicString { a := &AtomicString{}; a.Store(v); return a }, config, accessor) +func NewAtomicString[C any](config ConfigObserver[C], accessor func(*C) string) *atomic.String { + return newAtomic(atomic.NewString, config, accessor) } -func NewAtomicTime[C any](config ConfigObserver[C], accessor func(*C) time.Time) *AtomicTime { - return newAtomic(func(v time.Time) *AtomicTime { a := &AtomicTime{}; a.Store(v); return a }, config, accessor) +func NewAtomicTime[C any](config ConfigObserver[C], accessor func(*C) time.Time) *atomic.Time { + return newAtomic(atomic.NewTime, config, accessor) } func NewAtomicUint32[C any](config ConfigObserver[C], accessor func(*C) uint32) *atomic.Uint32 { - return newAtomic(func(v uint32) *atomic.Uint32 { a := &atomic.Uint32{}; a.Store(v); return a }, config, accessor) + return newAtomic(atomic.NewUint32, config, accessor) } func NewAtomicUint64[C any](config ConfigObserver[C], accessor func(*C) uint64) *atomic.Uint64 { - return newAtomic(func(v uint64) *atomic.Uint64 { a := &atomic.Uint64{}; a.Store(v); return a }, config, accessor) + return newAtomic(atomic.NewUint64, config, accessor) } func NewAtomicPointer[T, C any](config ConfigObserver[C], accessor func(*C) *T) *atomic.Pointer[T] { - return newAtomic(func(v *T) *atomic.Pointer[T] { a := &atomic.Pointer[T]{}; a.Store(v); return a }, config, accessor) + return newAtomic(atomic.NewPointer[T], config, accessor) } type AtomicValue[T any] struct { diff --git a/utils/hedge_test.go b/utils/hedge_test.go index 2728a8ad0..fdf86d41c 100644 --- a/utils/hedge_test.go +++ b/utils/hedge_test.go @@ -21,7 +21,7 @@ import ( "time" "github.com/stretchr/testify/require" - "sync/atomic" + "go.uber.org/atomic" ) func TestHedgeCall(t *testing.T) { diff --git a/utils/hwstats/cpu.go b/utils/hwstats/cpu.go index 89bedf7bf..97e9ab700 100644 --- a/utils/hwstats/cpu.go +++ b/utils/hwstats/cpu.go @@ -15,28 +15,18 @@ package hwstats import ( - "math" "os" "strconv" "sync" - "sync/atomic" "time" "github.com/frostbyte73/core" "github.com/prometheus/procfs" + "go.uber.org/atomic" "github.com/livekit/protocol/logger" ) -// atomicFloat64 stores a float64 in a sync/atomic.Uint64 via its IEEE-754 bit -// representation. sync/atomic has no Float64. -type atomicFloat64 struct { - v atomic.Uint64 -} - -func (a *atomicFloat64) Load() float64 { return math.Float64frombits(a.v.Load()) } -func (a *atomicFloat64) Store(v float64) { a.v.Store(math.Float64bits(v)) } - // This object returns cgroup quota aware cpu stats. On other systems than Linux, // it falls back to full system stats @@ -73,7 +63,7 @@ type platformCPUMonitor interface { } type CPUStats struct { - idleCPUs atomicFloat64 + idleCPUs atomic.Float64 platform platformCPUMonitor idleCallback func(idle float64) diff --git a/utils/multitonservice_test.go b/utils/multitonservice_test.go index a02d23796..263f57c0f 100644 --- a/utils/multitonservice_test.go +++ b/utils/multitonservice_test.go @@ -19,7 +19,7 @@ import ( "time" "github.com/stretchr/testify/require" - "sync/atomic" + "go.uber.org/atomic" ) type testService struct { @@ -31,7 +31,7 @@ func newTestService(key string) *testService { return &testService{key: key} } -func (s *testService) Kill() { s.killCalls.Add(1) } +func (s *testService) Kill() { s.killCalls.Inc() } func TestMultitonService(t *testing.T) { t.Run("start and stop are called", func(t *testing.T) { diff --git a/utils/parallel.go b/utils/parallel.go index caa4135ee..1593ed9ff 100644 --- a/utils/parallel.go +++ b/utils/parallel.go @@ -17,7 +17,8 @@ package utils import ( "runtime" "sync" - "sync/atomic" + + "go.uber.org/atomic" ) // ParallelExec will executes the given function with each element of vals, if len(vals) >= parallelThreshold, @@ -31,7 +32,7 @@ func ParallelExec[T any](vals []T, parallelThreshold, step uint64, fn func(T)) { } // parallel - enables much more efficient multi-core utilization - var start atomic.Uint64 + start := atomic.NewUint64(0) end := uint64(len(vals)) var wg sync.WaitGroup diff --git a/utils/rate.go b/utils/rate.go index 81149ec9c..266dced7e 100644 --- a/utils/rate.go +++ b/utils/rate.go @@ -27,7 +27,7 @@ import ( "sync" "time" - "sync/atomic" + "go.uber.org/atomic" ) type LeakyBucket struct { diff --git a/utils/rate_test.go b/utils/rate_test.go index 79225590f..c523a37ac 100644 --- a/utils/rate_test.go +++ b/utils/rate_test.go @@ -29,7 +29,7 @@ import ( "testing" "time" - "sync/atomic" + "go.uber.org/atomic" "github.com/benbjohnson/clock" "github.com/stretchr/testify/require" @@ -250,7 +250,7 @@ func (r *runnerImpl) startTaking(rls ...Limiter) { for _, rl := range rls { rl.Take() } - r.count.Add(1) + r.count.Inc() select { case <-r.doneCh: return @@ -265,7 +265,7 @@ func (r *runnerImpl) takeOnceAfter(d time.Duration, rl Limiter) { r.wg.Add(1) r.afterFunc(d, func() { rl.Take() - r.count.Add(1) + r.count.Inc() r.wg.Done() }) } diff --git a/webhook/url_notifier.go b/webhook/url_notifier.go index 1c97c1839..ee96c376f 100644 --- a/webhook/url_notifier.go +++ b/webhook/url_notifier.go @@ -27,7 +27,7 @@ import ( "github.com/frostbyte73/core" "github.com/hashicorp/go-retryablehttp" - "sync/atomic" + "go.uber.org/atomic" "github.com/livekit/protocol/auth" "github.com/livekit/protocol/livekit" @@ -224,7 +224,7 @@ func (n *URLNotifier) QueueNotify(ctx context.Context, event *livekit.WebhookEve ph(ctx, whi) } }) { - n.dropped.Add(1) + n.dropped.Inc() fields := logFields(event, params.URL) params.Logger.Infow("dropped webhook", fields...) diff --git a/webhook/webhook_test.go b/webhook/webhook_test.go index e5e97ab26..bf225bc18 100644 --- a/webhook/webhook_test.go +++ b/webhook/webhook_test.go @@ -23,10 +23,9 @@ import ( "testing" "time" - "sync/atomic" - "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/require" + "go.uber.org/atomic" "github.com/livekit/protocol/auth" "github.com/livekit/protocol/livekit" @@ -101,7 +100,7 @@ func TestURLNotifierDropped(t *testing.T) { s.handler = func(w http.ResponseWriter, r *http.Request) { decodedEvent, err := ReceiveWebhookEvent(r, authProvider) require.NoError(t, err) - totalReceived.Add(1) + totalReceived.Inc() totalDropped.Add(decodedEvent.NumDropped) } // send multiple notifications @@ -134,7 +133,7 @@ func TestURLNotifierLifecycle(t *testing.T) { urlNotifier := newTestNotifier() numCalled := atomic.Int32{} s.handler = func(w http.ResponseWriter, r *http.Request) { - numCalled.Add(1) + numCalled.Inc() } for i := 0; i < 10; i++ { _ = urlNotifier.QueueNotify(context.Background(), &livekit.WebhookEvent{Event: EventRoomStarted}) @@ -148,7 +147,7 @@ func TestURLNotifierLifecycle(t *testing.T) { urlNotifier := newTestNotifier() numCalled := atomic.Int32{} s.handler = func(w http.ResponseWriter, r *http.Request) { - numCalled.Add(1) + numCalled.Inc() } for i := 0; i < 10; i++ { _ = urlNotifier.QueueNotify(context.Background(), &livekit.WebhookEvent{Event: EventRoomStarted}) @@ -183,7 +182,7 @@ func TestURLNotifierLifecycle(t *testing.T) { time.Sleep(time.Second) if r.Context().Err() == nil { // inc if not canceled - numCalled.Add(1) + numCalled.Inc() } } defer urlNotifier.Stop(false) @@ -238,7 +237,7 @@ func TestURLNotifierFilter(t *testing.T) { numCalled := atomic.Int32{} s.handler = func(w http.ResponseWriter, r *http.Request) { - numCalled.Add(1) + numCalled.Inc() } _ = urlNotifier.QueueNotify(context.Background(), &livekit.WebhookEvent{Event: EventRoomStarted}) @@ -269,7 +268,7 @@ func TestURLNotifierFilter(t *testing.T) { numCalled := atomic.Int32{} s.handler = func(w http.ResponseWriter, r *http.Request) { - numCalled.Add(1) + numCalled.Inc() } _ = urlNotifier.QueueNotify(context.Background(), &livekit.WebhookEvent{Event: EventRoomStarted}) @@ -300,7 +299,7 @@ func TestURLNotifierFilter(t *testing.T) { numCalled := atomic.Int32{} s.handler = func(w http.ResponseWriter, r *http.Request) { - numCalled.Add(1) + numCalled.Inc() } _ = urlNotifier.QueueNotify(context.Background(), &livekit.WebhookEvent{Event: EventRoomStarted}) @@ -332,7 +331,7 @@ func TestURLNotifierFilter(t *testing.T) { numCalled := atomic.Int32{} s.handler = func(w http.ResponseWriter, r *http.Request) { - numCalled.Add(1) + numCalled.Inc() } // EventRoomStarted should be allowed as IncludeEvents take precedence @@ -416,21 +415,21 @@ func TestResourceURLNotifierDropped(t *testing.T) { s.handler = func(w http.ResponseWriter, r *http.Request) { _, err := ReceiveWebhookEvent(r, authProvider) require.NoError(t, err) - totalReceived.Add(1) + totalReceived.Inc() } // send multiple notifications for i := 0; i < 10; i++ { err := resourceURLNotifier.QueueNotify(context.Background(), &livekit.WebhookEvent{Event: EventRoomStarted}) if err == errQueueFull { - totalDropped.Add(1) + totalDropped.Inc() } err = resourceURLNotifier.QueueNotify(context.Background(), &livekit.WebhookEvent{Event: EventParticipantJoined}) if err == errQueueFull { - totalDropped.Add(1) + totalDropped.Inc() } err = resourceURLNotifier.QueueNotify(context.Background(), &livekit.WebhookEvent{Event: EventRoomFinished}) if err == errQueueFull { - totalDropped.Add(1) + totalDropped.Inc() } } @@ -457,7 +456,7 @@ func TestResourceURLNotifierDropped(t *testing.T) { time.Sleep(5 * time.Millisecond) _, err := ReceiveWebhookEvent(r, authProvider) require.NoError(t, err) - totalReceived.Add(1) + totalReceived.Inc() } // send multiple notifications for i := 0; i < 10; i++ { @@ -483,7 +482,7 @@ func TestResourceURLNotifierDropped(t *testing.T) { s.handler = func(w http.ResponseWriter, r *http.Request) { _, err := ReceiveWebhookEvent(r, authProvider) require.NoError(t, err) - totalReceived.Add(1) + totalReceived.Inc() } // check that resource queues change for the same event key @@ -567,7 +566,7 @@ func TestResourceURLNotifierLifecycle(t *testing.T) { resourceURLNotifier := newTestResourceNotifier(200*time.Millisecond, 200*time.Millisecond, 50) numCalled := atomic.Int32{} s.handler = func(w http.ResponseWriter, r *http.Request) { - numCalled.Add(1) + numCalled.Inc() } for i := 0; i < 10; i++ { roomName := fmt.Sprintf("room%d", i) @@ -610,7 +609,7 @@ func TestResourceURLNotifierLifecycle(t *testing.T) { resourceURLNotifier := newTestResourceNotifier(time.Minute, 200*time.Millisecond, 50) numCalled := atomic.Int32{} s.handler = func(w http.ResponseWriter, r *http.Request) { - numCalled.Add(1) + numCalled.Inc() } for i := 0; i < 10; i++ { _ = resourceURLNotifier.QueueNotify(context.Background(), &livekit.WebhookEvent{Event: EventRoomStarted}) @@ -624,7 +623,7 @@ func TestResourceURLNotifierLifecycle(t *testing.T) { resourceURLNotifier := newTestResourceNotifier(time.Minute, 200*time.Millisecond, 50) numCalled := atomic.Int32{} s.handler = func(w http.ResponseWriter, r *http.Request) { - numCalled.Add(1) + numCalled.Inc() } for i := 0; i < 10; i++ { _ = resourceURLNotifier.QueueNotify(context.Background(), &livekit.WebhookEvent{Event: EventRoomStarted}) @@ -662,7 +661,7 @@ func TestResourceURLNotifierLifecycle(t *testing.T) { time.Sleep(time.Second) if r.Context().Err() == nil { // inc if not canceled - numCalled.Add(1) + numCalled.Inc() } } defer resourceURLNotifier.Stop(false) @@ -721,7 +720,7 @@ func TestResourceURLNotifierFilter(t *testing.T) { numCalled := atomic.Int32{} s.handler = func(w http.ResponseWriter, r *http.Request) { - numCalled.Add(1) + numCalled.Inc() } _ = resourceURLNotifier.QueueNotify(context.Background(), &livekit.WebhookEvent{Event: EventRoomStarted}) @@ -753,7 +752,7 @@ func TestResourceURLNotifierFilter(t *testing.T) { numCalled := atomic.Int32{} s.handler = func(w http.ResponseWriter, r *http.Request) { - numCalled.Add(1) + numCalled.Inc() } _ = resourceURLNotifier.QueueNotify(context.Background(), &livekit.WebhookEvent{Event: EventRoomStarted}) @@ -785,7 +784,7 @@ func TestResourceURLNotifierFilter(t *testing.T) { numCalled := atomic.Int32{} s.handler = func(w http.ResponseWriter, r *http.Request) { - numCalled.Add(1) + numCalled.Inc() } _ = resourceURLNotifier.QueueNotify(context.Background(), &livekit.WebhookEvent{Event: EventRoomStarted}) @@ -818,7 +817,7 @@ func TestResourceURLNotifierFilter(t *testing.T) { numCalled := atomic.Int32{} s.handler = func(w http.ResponseWriter, r *http.Request) { - numCalled.Add(1) + numCalled.Inc() } // EventRoomStarted should be allowed as IncludeEvents take precedence From 29568b8de2e3da71de041d72ed63b15d921f46b7 Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Thu, 4 Jun 2026 14:35:44 -0700 Subject: [PATCH 06/12] jwtutil: add helper for kid-based JWT key rotation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restores the convenience that go-jose's JSONWebKeySet provided when picking the right key from a set during verification, which golang-jwt/jwt/v5 leaves to the caller. The new utils/jwtutil package wraps the manual kid lookup + WithValidMethods enforcement that has to repeat at every signer/verifier site otherwise. Also fixes a stale ST1005 (capitalized error string) in redis/redis.go that surfaced after the pkg/errors → stdlib swap. --- redis/redis.go | 2 +- utils/jwtutil/jwtutil.go | 134 +++++++++++++++++++++++++ utils/jwtutil/jwtutil_test.go | 177 ++++++++++++++++++++++++++++++++++ 3 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 utils/jwtutil/jwtutil.go create mode 100644 utils/jwtutil/jwtutil_test.go diff --git a/redis/redis.go b/redis/redis.go index f98fccc40..6881ba1d0 100644 --- a/redis/redis.go +++ b/redis/redis.go @@ -28,7 +28,7 @@ import ( "github.com/livekit/protocol/logger" ) -var ErrNotConfigured = errors.New("Redis is not configured") +var ErrNotConfigured = errors.New("redis is not configured") type RedisConfig struct { Address string `yaml:"address,omitempty"` diff --git a/utils/jwtutil/jwtutil.go b/utils/jwtutil/jwtutil.go new file mode 100644 index 000000000..e9a0f6994 --- /dev/null +++ b/utils/jwtutil/jwtutil.go @@ -0,0 +1,134 @@ +// Copyright 2026 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 jwtutil offers shared helpers for signing and verifying JWTs with +// statically-configured multi-key sets — i.e. kid-based key rotation. It +// replaces the convenience that go-jose's JSONWebKeySet provided when picking +// the right key out of a set during verification, which golang-jwt/jwt/v5 +// leaves to the caller. +package jwtutil + +import ( + "crypto/sha256" + "encoding/base64" + "errors" + "fmt" + + "github.com/golang-jwt/jwt/v5" +) + +var ( + ErrEmptyKeySet = errors.New("jwtutil: key set is empty") + ErrDuplicateKeyID = errors.New("jwtutil: duplicate key ID") + ErrUnknownKeyID = errors.New("jwtutil: unknown signing key") + ErrSigningMethod = errors.New("jwtutil: unsupported signing method") +) + +// HMACKey is one entry in an HMACKeySet. +type HMACKey struct { + // ID is the value placed in (and matched against) the JWT's `kid` header. + ID string + // Key is the HMAC shared secret. + Key []byte +} + +// HMACKeyID derives a stable, opaque identifier for an HMAC key as the first +// 8 bytes of its SHA-256, base64(RawStdEncoding)-encoded. Use when keys are +// configured as opaque secrets without explicit IDs. +func HMACKeyID(key []byte) string { + h := sha256.Sum256(key) + return base64.RawStdEncoding.EncodeToString(h[:8]) +} + +// HMACKeySet is an ordered set of HMAC keys for signing and verifying JWTs +// with kid-based key rotation. The first key is the active signer; all keys +// in the set are accepted during verification, allowing graceful rotation +// without invalidating in-flight tokens. +type HMACKeySet struct { + method jwt.SigningMethod + keys []HMACKey + byID map[string][]byte +} + +// NewHMACKeySet builds an HMACKeySet for the given signing method. method +// must be one of jwt.SigningMethodHS256 / HS384 / HS512. The first key in +// keys is the active signer. +func NewHMACKeySet(method jwt.SigningMethod, keys ...HMACKey) (*HMACKeySet, error) { + switch method { + case jwt.SigningMethodHS256, jwt.SigningMethodHS384, jwt.SigningMethodHS512: + default: + return nil, fmt.Errorf("%w: %q", ErrSigningMethod, method.Alg()) + } + if len(keys) == 0 { + return nil, ErrEmptyKeySet + } + ks := &HMACKeySet{ + method: method, + keys: make([]HMACKey, len(keys)), + byID: make(map[string][]byte, len(keys)), + } + for i, k := range keys { + if _, exists := ks.byID[k.ID]; exists { + return nil, fmt.Errorf("%w: %q", ErrDuplicateKeyID, k.ID) + } + ks.keys[i] = k + ks.byID[k.ID] = k.Key + } + return ks, nil +} + +// NewHMACKeySetFromBytes is a convenience that derives each key's ID via +// HMACKeyID. Use when keys are configured as opaque secrets and the caller +// doesn't manage key IDs explicitly. +func NewHMACKeySetFromBytes(method jwt.SigningMethod, keys ...[]byte) (*HMACKeySet, error) { + hmacKeys := make([]HMACKey, len(keys)) + for i, k := range keys { + hmacKeys[i] = HMACKey{ID: HMACKeyID(k), Key: k} + } + return NewHMACKeySet(method, hmacKeys...) +} + +// SigningMethod returns the signing method enforced by this key set. +func (ks *HMACKeySet) SigningMethod() jwt.SigningMethod { + return ks.method +} + +// Sign returns a compact-serialized JWT signed with the active (first) key. +// The active key's ID is set as the `kid` header. +func (ks *HMACKeySet) Sign(claims jwt.Claims) (string, error) { + active := ks.keys[0] + tok := jwt.NewWithClaims(ks.method, claims) + tok.Header["kid"] = active.ID + return tok.SignedString(active.Key) +} + +// ParseWithClaims parses and verifies the token, selecting the signing key +// from the set by matching the token's `kid` header. The set's signing +// method is enforced via jwt.WithValidMethods. Additional parser options +// (WithIssuer, WithLeeway, WithExpirationRequired, …) may be passed. +func (ks *HMACKeySet) ParseWithClaims(token string, claims jwt.Claims, opts ...jwt.ParserOption) (*jwt.Token, error) { + allOpts := make([]jwt.ParserOption, 0, len(opts)+1) + allOpts = append(allOpts, jwt.WithValidMethods([]string{ks.method.Alg()})) + allOpts = append(allOpts, opts...) + return jwt.ParseWithClaims(token, claims, ks.keyFunc, allOpts...) +} + +func (ks *HMACKeySet) keyFunc(tok *jwt.Token) (any, error) { + kid, _ := tok.Header["kid"].(string) + key, ok := ks.byID[kid] + if !ok { + return nil, fmt.Errorf("%w: %q", ErrUnknownKeyID, kid) + } + return key, nil +} diff --git a/utils/jwtutil/jwtutil_test.go b/utils/jwtutil/jwtutil_test.go new file mode 100644 index 000000000..ea7ee46c4 --- /dev/null +++ b/utils/jwtutil/jwtutil_test.go @@ -0,0 +1,177 @@ +// Copyright 2026 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 jwtutil_test + +import ( + "crypto/sha256" + "encoding/base64" + "strings" + "testing" + "time" + + "github.com/golang-jwt/jwt/v5" + "github.com/stretchr/testify/require" + + "github.com/livekit/protocol/utils/jwtutil" +) + +func TestHMACKeyID_StableSHA256First8(t *testing.T) { + // Wire-compat with cloud-protocol/hostedagentsutil: id is base64(RawStdEncoding, sha256(key)[:8]). + key := []byte("the-secret") + want := func() string { + h := sha256.Sum256(key) + return base64.RawStdEncoding.EncodeToString(h[:8]) + }() + require.Equal(t, want, jwtutil.HMACKeyID(key)) +} + +func TestNewHMACKeySet_ValidatesMethod(t *testing.T) { + _, err := jwtutil.NewHMACKeySet(jwt.SigningMethodRS256, jwtutil.HMACKey{ID: "a", Key: []byte("k")}) + require.ErrorIs(t, err, jwtutil.ErrSigningMethod) +} + +func TestNewHMACKeySet_RejectsEmpty(t *testing.T) { + _, err := jwtutil.NewHMACKeySet(jwt.SigningMethodHS256) + require.ErrorIs(t, err, jwtutil.ErrEmptyKeySet) +} + +func TestNewHMACKeySet_RejectsDuplicateID(t *testing.T) { + _, err := jwtutil.NewHMACKeySet(jwt.SigningMethodHS256, + jwtutil.HMACKey{ID: "x", Key: []byte("k1")}, + jwtutil.HMACKey{ID: "x", Key: []byte("k2")}, + ) + require.ErrorIs(t, err, jwtutil.ErrDuplicateKeyID) +} + +func TestSignVerifyRoundTrip(t *testing.T) { + ks, err := jwtutil.NewHMACKeySet(jwt.SigningMethodHS256, jwtutil.HMACKey{ID: "a", Key: []byte("secret-a")}) + require.NoError(t, err) + + claims := jwt.RegisteredClaims{ + Issuer: "tester", + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute)), + } + tokStr, err := ks.Sign(&claims) + require.NoError(t, err) + + out := &jwt.RegisteredClaims{} + parsed, err := ks.ParseWithClaims(tokStr, out) + require.NoError(t, err) + require.Equal(t, "a", parsed.Header["kid"]) + require.Equal(t, "tester", out.Issuer) +} + +func TestRotation_NewSetAcceptsOldKID(t *testing.T) { + keyA := jwtutil.HMACKey{ID: "a", Key: []byte("secret-a")} + keyB := jwtutil.HMACKey{ID: "b", Key: []byte("secret-b")} + + signer, err := jwtutil.NewHMACKeySet(jwt.SigningMethodHS256, keyA, keyB) + require.NoError(t, err) + tokStr, err := signer.Sign(&jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute)), + }) + require.NoError(t, err) + + // Rotate: B is now the active signer but A is still accepted. + verifier, err := jwtutil.NewHMACKeySet(jwt.SigningMethodHS256, keyB, keyA) + require.NoError(t, err) + out := &jwt.RegisteredClaims{} + _, err = verifier.ParseWithClaims(tokStr, out) + require.NoError(t, err) +} + +func TestRotation_OldVerifierRejectsNewKID(t *testing.T) { + keyA := jwtutil.HMACKey{ID: "a", Key: []byte("secret-a")} + keyB := jwtutil.HMACKey{ID: "b", Key: []byte("secret-b")} + + // New signer uses key B; old verifier only knows A. + signer, err := jwtutil.NewHMACKeySet(jwt.SigningMethodHS256, keyB) + require.NoError(t, err) + tokStr, err := signer.Sign(&jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute)), + }) + require.NoError(t, err) + + verifier, err := jwtutil.NewHMACKeySet(jwt.SigningMethodHS256, keyA) + require.NoError(t, err) + _, err = verifier.ParseWithClaims(tokStr, &jwt.RegisteredClaims{}) + require.Error(t, err) + require.ErrorIs(t, err, jwtutil.ErrUnknownKeyID) +} + +func TestVerify_RejectsTokenSignedWithWrongKeyBytes(t *testing.T) { + // Two sets share a kid ("a") but the actual key bytes differ — verify must + // reject the foreign signature instead of trusting the kid alone. + signer, err := jwtutil.NewHMACKeySet(jwt.SigningMethodHS256, jwtutil.HMACKey{ID: "a", Key: []byte("attacker-key")}) + require.NoError(t, err) + tokStr, err := signer.Sign(&jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute)), + }) + require.NoError(t, err) + + verifier, err := jwtutil.NewHMACKeySet(jwt.SigningMethodHS256, jwtutil.HMACKey{ID: "a", Key: []byte("real-key")}) + require.NoError(t, err) + _, err = verifier.ParseWithClaims(tokStr, &jwt.RegisteredClaims{}) + require.Error(t, err) + require.ErrorIs(t, err, jwt.ErrSignatureInvalid) +} + +func TestVerify_EnforcesSigningMethod(t *testing.T) { + // HS256 verifier rejects an HS512-signed token even when the kid + secret match. + key := jwtutil.HMACKey{ID: "a", Key: []byte("secret")} + tok512 := jwt.NewWithClaims(jwt.SigningMethodHS512, &jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute)), + }) + tok512.Header["kid"] = key.ID + tokStr, err := tok512.SignedString(key.Key) + require.NoError(t, err) + + verifier, err := jwtutil.NewHMACKeySet(jwt.SigningMethodHS256, key) + require.NoError(t, err) + _, err = verifier.ParseWithClaims(tokStr, &jwt.RegisteredClaims{}) + require.Error(t, err) + require.True(t, strings.Contains(err.Error(), "signing method"), "got %v", err) +} + +func TestParseWithClaims_ExtraOptionsApplied(t *testing.T) { + key := jwtutil.HMACKey{ID: "a", Key: []byte("secret")} + ks, err := jwtutil.NewHMACKeySet(jwt.SigningMethodHS256, key) + require.NoError(t, err) + + tokStr, err := ks.Sign(&jwt.RegisteredClaims{ + Issuer: "expected-issuer", + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute)), + }) + require.NoError(t, err) + + _, err = ks.ParseWithClaims(tokStr, &jwt.RegisteredClaims{}, jwt.WithIssuer("not-this-issuer")) + require.Error(t, err) + require.ErrorIs(t, err, jwt.ErrTokenInvalidIssuer) +} + +func TestNewHMACKeySetFromBytes_DerivesIDs(t *testing.T) { + ks, err := jwtutil.NewHMACKeySetFromBytes(jwt.SigningMethodHS256, []byte("k1"), []byte("k2")) + require.NoError(t, err) + + tokStr, err := ks.Sign(&jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute)), + }) + require.NoError(t, err) + + // kid should match HMACKeyID("k1") (the active key) + parsed, err := ks.ParseWithClaims(tokStr, &jwt.RegisteredClaims{}) + require.NoError(t, err) + require.Equal(t, jwtutil.HMACKeyID([]byte("k1")), parsed.Header["kid"]) +} From 6b0178b3af2a376b2163e88adb50f5c70e4bee2a Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Thu, 4 Jun 2026 16:38:57 -0700 Subject: [PATCH 07/12] deps: bump psrpc to deps/protocol-major-updates HEAD --- go.mod | 6 ++--- go.sum | 72 +++++++++++++++++++++------------------------------------- 2 files changed, 29 insertions(+), 49 deletions(-) diff --git a/go.mod b/go.mod index c8044bc28..f2eaa8d7d 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/jxskiss/base62 v1.1.0 github.com/lithammer/shortuuid/v4 v4.2.0 github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731 - github.com/livekit/psrpc v0.7.1 + github.com/livekit/psrpc v0.7.2-0.20260604225640-4bab4033deca github.com/mackerelio/go-osstat v0.2.5 github.com/maxbrunsfeld/counterfeiter/v6 v6.11.1 github.com/nyaruka/phonenumbers v1.6.5 @@ -55,7 +55,7 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/google/cel-go v0.28.1 // indirect github.com/google/uuid v1.6.0 // indirect @@ -80,7 +80,7 @@ require ( github.com/pion/stun/v3 v3.1.4 // indirect github.com/pion/transport/v4 v4.0.2 // indirect github.com/pion/turn/v5 v5.0.8 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + 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.64.0 // indirect github.com/wlynxg/anet v0.0.5 // indirect diff --git a/go.sum b/go.sum index ef8ea6471..d0f52a2ff 100644 --- a/go.sum +++ b/go.sum @@ -6,14 +6,8 @@ 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= -dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= -dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -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= 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= @@ -26,28 +20,28 @@ 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= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8= -github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= -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/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/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/docker/cli v26.1.4+incompatible h1:I8PHdc0MtxEADqYJZvhBrW9bo8gawKwwenxRM7/rLu8= -github.com/docker/cli v26.1.4+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/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 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.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho= @@ -59,8 +53,6 @@ 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/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= @@ -69,8 +61,6 @@ 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.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/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk= @@ -95,8 +85,8 @@ github.com/lithammer/shortuuid/v4 v4.2.0 h1:LMFOzVB3996a7b8aBuEXxqOBflbfPQAiVzkI 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/psrpc v0.7.1 h1:ms37az0QTD3UXIWuUC5D/SkmKOlRMVRsI261eBWu/Vw= -github.com/livekit/psrpc v0.7.1/go.mod h1:bZ4iHFQptTkbPnB0LasvRNu/OBYXEu1NA6O5BMFo9kk= +github.com/livekit/psrpc v0.7.2-0.20260604225640-4bab4033deca h1:d6itWQ1sp5wkUPsgo7k2hNHbZyDp18NtSJg1B6lNJKA= +github.com/livekit/psrpc v0.7.2-0.20260604225640-4bab4033deca/go.mod h1:rAI+m2+/cb4x9RXhLRtUx5ZwdfjjXOl4zi46IjEetaw= github.com/mackerelio/go-osstat v0.2.5 h1:+MqTbZUhoIt4m8qzkVoXUJg1EuifwlAJSk4Yl2GXh+o= github.com/mackerelio/go-osstat v0.2.5/go.mod h1:atxwWF+POUZcdtR1wnsUcQxTytoHG4uhl2AKKzrOajY= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -105,12 +95,12 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/maxbrunsfeld/counterfeiter/v6 v6.11.1 h1:sLhPA/17T2R7BX+soElaweEJHKMFxI5/94Ovps8Xbbs= github.com/maxbrunsfeld/counterfeiter/v6 v6.11.1/go.mod h1:pYds9shqqVnjSuIwEBLyOl9hy5uJeMarcdRFK9B5Xfk= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 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/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.52.0 h1:n3avV4VBsCgsdwh71TppsTwtv+QdPs7ntSKM8qJLGsc= @@ -125,12 +115,10 @@ github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= 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.1.13 h1:98S2srgG9vw0zWcDpFMn5TRrh8kLxa/5OFUstuUhmRs= -github.com/opencontainers/runc v1.1.13/go.mod h1:R016aXacfp/gwQBYw2FDGa9m+n6atbLWrYY8hNMT/sA= -github.com/ory/dockertest/v3 v3.11.0 h1:OiHcxKAvSDUwsEVh2BjxQQc/5EHz9n0va9awCtNGuyA= -github.com/ory/dockertest/v3 v3.11.0/go.mod h1:VIPxS1gwT9NpPOrfD3rACs8Y9Z7yhzO4SB194iUDnUI= +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.3 h1:OA6J5UCeA8DvRXD8ofaMnlNPXN3ISBLHHJ9P8SWL09E= @@ -167,8 +155,8 @@ github.com/pion/webrtc/v4 v4.2.14 h1:Q6zMs+fSDsYuhZcNlvFGBxCOMHVV9oYcDa6O9/HIGTc github.com/pion/webrtc/v4 v4.2.14/go.mod h1:87NVKP86+g4OMrRxWhjWfUjeXP4JrV6RTlUrIW+/Jak= 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.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= @@ -189,26 +177,20 @@ 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/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= 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/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-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/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= 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/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.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k= @@ -268,7 +250,5 @@ google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From a7a83da5bb556c4ab9ae2e5c8c5a97ec06b2520a Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Thu, 4 Jun 2026 18:29:06 -0700 Subject: [PATCH 08/12] deps: go get -u sweep + bump counterfeiter to v6.12.2 Direct deps bumped: hashicorp/go-retryablehttp 0.7.7 -> 0.7.8 mackerelio/go-osstat 0.2.5 -> 0.2.7 maxbrunsfeld/counterfeiter/v6 v6.11.1 -> v6.12.2 nyaruka/phonenumbers v1.6.5 -> v1.8.0 prometheus/client_golang 1.22.0 -> 1.23.2 prometheus/procfs 0.16.1 -> 0.20.1 otel/exporters/otlp/otlptrace/otlptracehttp + sdk 1.43.0 -> 1.44.0 No newer major versions actionable. phonenumbers v2.0.0-rc1 exists but is release-candidate only. twitchtv/twirp v8.1.3+incompatible stays (upstream has no module-aware path). --- go.mod | 20 ++++++++++---------- go.sum | 50 ++++++++++++++++++++++++++------------------------ 2 files changed, 36 insertions(+), 34 deletions(-) diff --git a/go.mod b/go.mod index f2eaa8d7d..9fa4d1583 100644 --- a/go.mod +++ b/go.mod @@ -11,27 +11,27 @@ require ( github.com/gammazero/deque v1.2.1 github.com/go-logr/logr v1.4.3 github.com/golang-jwt/jwt/v5 v5.3.1 - github.com/hashicorp/go-retryablehttp v0.7.7 + github.com/hashicorp/go-retryablehttp v0.7.8 github.com/jxskiss/base62 v1.1.0 github.com/lithammer/shortuuid/v4 v4.2.0 github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731 github.com/livekit/psrpc v0.7.2-0.20260604225640-4bab4033deca - github.com/mackerelio/go-osstat v0.2.5 - github.com/maxbrunsfeld/counterfeiter/v6 v6.11.1 - github.com/nyaruka/phonenumbers v1.6.5 + github.com/mackerelio/go-osstat v0.2.7 + github.com/maxbrunsfeld/counterfeiter/v6 v6.12.2 + github.com/nyaruka/phonenumbers v1.8.0 github.com/pion/logging v0.2.4 github.com/pion/sdp/v3 v3.0.18 github.com/pion/webrtc/v4 v4.2.14 - github.com/prometheus/client_golang v1.22.0 - github.com/prometheus/procfs v0.16.1 + github.com/prometheus/client_golang v1.23.2 + github.com/prometheus/procfs v0.20.1 github.com/puzpuzpuz/xsync/v4 v4.5.0 github.com/redis/go-redis/v9 v9.20.0 github.com/stretchr/testify v1.11.1 github.com/twitchtv/twirp v8.1.3+incompatible github.com/zeebo/xxh3 v1.1.0 go.opentelemetry.io/otel v1.44.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 - go.opentelemetry.io/otel/sdk v1.43.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.44.0 + go.opentelemetry.io/otel/sdk v1.44.0 go.opentelemetry.io/otel/trace v1.44.0 go.uber.org/atomic v1.11.0 go.uber.org/multierr v1.11.0 @@ -82,10 +82,10 @@ require ( github.com/pion/turn/v5 v5.0.8 // indirect 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.64.0 // indirect + github.com/prometheus/common v0.68.1 // indirect github.com/wlynxg/anet v0.0.5 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 // indirect go.opentelemetry.io/otel/metric v1.44.0 // indirect go.opentelemetry.io/proto/otlp v1.10.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect diff --git a/go.sum b/go.sum index d0f52a2ff..bf22e334d 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ 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-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= +github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= 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/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao= @@ -87,14 +87,14 @@ github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731 h1:9x+U2HGLrSw5AT github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731/go.mod h1:Rs3MhFwutWhGwmY1VQsygw28z5bWcnEYmS1OG9OxjOQ= github.com/livekit/psrpc v0.7.2-0.20260604225640-4bab4033deca h1:d6itWQ1sp5wkUPsgo7k2hNHbZyDp18NtSJg1B6lNJKA= github.com/livekit/psrpc v0.7.2-0.20260604225640-4bab4033deca/go.mod h1:rAI+m2+/cb4x9RXhLRtUx5ZwdfjjXOl4zi46IjEetaw= -github.com/mackerelio/go-osstat v0.2.5 h1:+MqTbZUhoIt4m8qzkVoXUJg1EuifwlAJSk4Yl2GXh+o= -github.com/mackerelio/go-osstat v0.2.5/go.mod h1:atxwWF+POUZcdtR1wnsUcQxTytoHG4uhl2AKKzrOajY= +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/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 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/maxbrunsfeld/counterfeiter/v6 v6.11.1 h1:sLhPA/17T2R7BX+soElaweEJHKMFxI5/94Ovps8Xbbs= -github.com/maxbrunsfeld/counterfeiter/v6 v6.11.1/go.mod h1:pYds9shqqVnjSuIwEBLyOl9hy5uJeMarcdRFK9B5Xfk= +github.com/maxbrunsfeld/counterfeiter/v6 v6.12.2 h1:V23nK2R2B63g2GhygF9zVGpnigmhvoZoH8d0hrZwMGY= +github.com/maxbrunsfeld/counterfeiter/v6 v6.12.2/go.mod h1:Mr897yU9FmyKaQDPtRlVKibrjz40XXyOHUfyZBPSyZU= 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/moby/api v1.54.2 h1:wiat9QAhnDQjA7wk1kh/TqHz2I1uUA7M7t9SAl/JNXg= @@ -109,10 +109,10 @@ 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/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= -github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/nyaruka/phonenumbers v1.8.0 h1:TrXNJmbwcAHajzDqin3mLWw57vqLUA6ZjVdeNds0heQ= +github.com/nyaruka/phonenumbers v1.8.0/go.mod h1:fsKPJ70O9JetEA4ggnJadYTFWwtGPvu/lETTXNXq6Cs= +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.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= @@ -157,14 +157,14 @@ 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.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.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= -github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +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.64.0 h1:pdZeA+g617P7oGv1CzdTzyeShxAGrTBsolKNOLQPGO4= -github.com/prometheus/common v0.64.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= -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/v4 v4.5.0 h1:vOSWu6b57/emh+L/Cw0BeQfvxa/cogFywXHeGUxQxAg= github.com/puzpuzpuz/xsync/v4 v4.5.0/go.mod h1:VJDmTCJMBt8igNxnkQd86r+8KUeN1quSfNKu5bLYFQo= github.com/redis/go-redis/v9 v9.20.0 h1:WnQYxLkgO2xiXTCJY0ldIiI8dNqCDlQAG+AtaH7a2a0= @@ -193,16 +193,16 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 h1:8tvICD4 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.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/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.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/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= @@ -217,6 +217,8 @@ 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.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.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= From 964d1f0c60998f6a75b26c5b5cb20e1f9e4d2502 Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Fri, 5 Jun 2026 00:01:29 -0700 Subject: [PATCH 09/12] jwtutil: add go-jose v4 wire-compat interop tests Verifies that compact JWTs produced by jwtutil's HMACKeySet are still readable by go-jose v4 consumers, and vice versa, after the switch from go-jose/v4 to golang-jwt/jwt/v5. go-jose/v4 is added as a test-only dependency; the 32-byte key satisfies its HS256 minimum. --- go.mod | 1 + go.sum | 2 + utils/jwtutil/jwtutil_gojose_interop_test.go | 74 ++++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 utils/jwtutil/jwtutil_gojose_interop_test.go diff --git a/go.mod b/go.mod index 9fa4d1583..b7e27e562 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/frostbyte73/core v0.1.1 github.com/fsnotify/fsnotify v1.10.1 github.com/gammazero/deque v1.2.1 + github.com/go-jose/go-jose/v4 v4.1.4 github.com/go-logr/logr v1.4.3 github.com/golang-jwt/jwt/v5 v5.3.1 github.com/hashicorp/go-retryablehttp v0.7.8 diff --git a/go.sum b/go.sum index bf22e334d..ced0c605d 100644 --- a/go.sum +++ b/go.sum @@ -48,6 +48,8 @@ github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx5 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/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= +github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= diff --git a/utils/jwtutil/jwtutil_gojose_interop_test.go b/utils/jwtutil/jwtutil_gojose_interop_test.go new file mode 100644 index 000000000..6f4f997d7 --- /dev/null +++ b/utils/jwtutil/jwtutil_gojose_interop_test.go @@ -0,0 +1,74 @@ +// Copyright 2026 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 jwtutil_test + +import ( + "testing" + "time" + + jose "github.com/go-jose/go-jose/v4" + josejwt "github.com/go-jose/go-jose/v4/jwt" + "github.com/golang-jwt/jwt/v5" + "github.com/stretchr/testify/require" + + "github.com/livekit/protocol/utils/jwtutil" +) + +// HS256 in go-jose v4 enforces RFC 7518's 32-byte HMAC key minimum. +var interopKey = []byte("interop-secret-padded-to-32-bytes!!") + +func TestGoJoseInterop_GoJoseSignsJwtutilVerifies(t *testing.T) { + signerOpts := (&jose.SignerOptions{}).WithType("JWT").WithHeader("kid", "a") + joseSigner, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: interopKey}, signerOpts) + require.NoError(t, err) + + exp := time.Now().Add(time.Minute).Truncate(time.Second) + tokStr, err := josejwt.Signed(joseSigner).Claims(josejwt.Claims{ + Issuer: "go-jose", + Expiry: josejwt.NewNumericDate(exp), + }).Serialize() + require.NoError(t, err) + + ks, err := jwtutil.NewHMACKeySet(jwt.SigningMethodHS256, jwtutil.HMACKey{ID: "a", Key: interopKey}) + require.NoError(t, err) + out := &jwt.RegisteredClaims{} + parsed, err := ks.ParseWithClaims(tokStr, out) + require.NoError(t, err) + require.Equal(t, "a", parsed.Header["kid"]) + require.Equal(t, "go-jose", out.Issuer) + require.True(t, out.ExpiresAt.Equal(exp)) +} + +func TestGoJoseInterop_JwtutilSignsGoJoseVerifies(t *testing.T) { + ks, err := jwtutil.NewHMACKeySet(jwt.SigningMethodHS256, jwtutil.HMACKey{ID: "a", Key: interopKey}) + require.NoError(t, err) + + exp := time.Now().Add(time.Minute).Truncate(time.Second) + tokStr, err := ks.Sign(&jwt.RegisteredClaims{ + Issuer: "jwtutil", + ExpiresAt: jwt.NewNumericDate(exp), + }) + require.NoError(t, err) + + parsed, err := josejwt.ParseSigned(tokStr, []jose.SignatureAlgorithm{jose.HS256}) + require.NoError(t, err) + require.Len(t, parsed.Headers, 1) + require.Equal(t, "a", parsed.Headers[0].KeyID) + + out := josejwt.Claims{} + require.NoError(t, parsed.Claims(interopKey, &out)) + require.Equal(t, "jwtutil", out.Issuer) + require.True(t, out.Expiry.Time().Equal(exp)) +} From 7f0893ab5bf27003d3e04628d19bf80f3fbdb934 Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Fri, 5 Jun 2026 11:17:25 -0700 Subject: [PATCH 10/12] deps: pin pion/webrtc/v4 v4.2.11 + pion/sctp v1.9.5 pion/sctp v1.10.0 (and the paired pion/webrtc/v4 v4.2.14 that needs its new API) causes a regression in livekit-server's TestDataPublishSlowSubscriber. Pinning here so downstream consumers pick the safe versions via MVS. --- go.mod | 4 ++-- go.sum | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index b7e27e562..32d7e5275 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/nyaruka/phonenumbers v1.8.0 github.com/pion/logging v0.2.4 github.com/pion/sdp/v3 v3.0.18 - github.com/pion/webrtc/v4 v4.2.14 + github.com/pion/webrtc/v4 v4.2.11 github.com/prometheus/client_golang v1.23.2 github.com/prometheus/procfs v0.20.1 github.com/puzpuzpuz/xsync/v4 v4.5.0 @@ -76,7 +76,7 @@ require ( github.com/pion/randutil v0.1.0 // indirect github.com/pion/rtcp v1.2.16 // indirect github.com/pion/rtp v1.10.2 // indirect - github.com/pion/sctp v1.10.0 // indirect + github.com/pion/sctp v1.9.5 // indirect github.com/pion/srtp/v3 v3.0.11 // indirect github.com/pion/stun/v3 v3.1.4 // indirect github.com/pion/transport/v4 v4.0.2 // indirect diff --git a/go.sum b/go.sum index ced0c605d..92677df8f 100644 --- a/go.sum +++ b/go.sum @@ -139,8 +139,8 @@ 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.2 h1:l+f6tTDcAH6xwepaAoW791ddhuYsJlqRATOzirO04Mo= github.com/pion/rtp v1.10.2/go.mod h1:Au8fc6cEByy8RLTwKTQTEeQqDB/SJDxwL4mZuxYA5Pk= -github.com/pion/sctp v1.10.0 h1:qeoD6swF/2M5bYRcAGayqSbTKX3m4AW29CiQxG1+Pfg= -github.com/pion/sctp v1.10.0/go.mod h1:N20Dq6LY+JvJDAh9VVh1JELngb2rQ8dPgds5yBWiPgw= +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.11 h1:GiESUr54/K4UuPigfq/CvWUed80JenQAHXn0C2MQQIQ= @@ -151,10 +151,12 @@ github.com/pion/transport/v3 v3.1.1 h1:Tr684+fnnKlhPceU+ICdrw6KKkTms+5qHMgw6bIkY github.com/pion/transport/v3 v3.1.1/go.mod h1:+c2eewC5WJQHiAA46fkMMzoYZSuGzA/7E2FPrOYHctQ= 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.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.14 h1:Q6zMs+fSDsYuhZcNlvFGBxCOMHVV9oYcDa6O9/HIGTc= -github.com/pion/webrtc/v4 v4.2.14/go.mod h1:87NVKP86+g4OMrRxWhjWfUjeXP4JrV6RTlUrIW+/Jak= +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.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= From 705d6331af1f9777c37347740bcfc66e425724cb Mon Sep 17 00:00:00 2001 From: Paul Wells Date: Fri, 5 Jun 2026 14:16:11 -0700 Subject: [PATCH 11/12] deps: pin psrpc to v0.7.2 release tag --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 32d7e5275..7aad1df98 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/jxskiss/base62 v1.1.0 github.com/lithammer/shortuuid/v4 v4.2.0 github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731 - github.com/livekit/psrpc v0.7.2-0.20260604225640-4bab4033deca + github.com/livekit/psrpc v0.7.2 github.com/mackerelio/go-osstat v0.2.7 github.com/maxbrunsfeld/counterfeiter/v6 v6.12.2 github.com/nyaruka/phonenumbers v1.8.0 diff --git a/go.sum b/go.sum index 92677df8f..23714658a 100644 --- a/go.sum +++ b/go.sum @@ -89,6 +89,8 @@ github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731 h1:9x+U2HGLrSw5AT github.com/livekit/mageutil v0.0.0-20250511045019-0f1ff63f7731/go.mod h1:Rs3MhFwutWhGwmY1VQsygw28z5bWcnEYmS1OG9OxjOQ= github.com/livekit/psrpc v0.7.2-0.20260604225640-4bab4033deca h1:d6itWQ1sp5wkUPsgo7k2hNHbZyDp18NtSJg1B6lNJKA= github.com/livekit/psrpc v0.7.2-0.20260604225640-4bab4033deca/go.mod h1:rAI+m2+/cb4x9RXhLRtUx5ZwdfjjXOl4zi46IjEetaw= +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/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= From 5e03336d0f2aa78eea244b7cb940fd4b91c908ce Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2026 21:18:05 +0000 Subject: [PATCH 12/12] generated protobuf --- rpc/agent.psrpc.go | 2 +- rpc/agent_dispatch.psrpc.go | 2 +- rpc/egress.psrpc.go | 2 +- rpc/ingress.psrpc.go | 2 +- rpc/io.psrpc.go | 2 +- rpc/keepalive.psrpc.go | 2 +- rpc/participant.psrpc.go | 2 +- rpc/room.psrpc.go | 2 +- rpc/roommanager.psrpc.go | 2 +- rpc/signal.psrpc.go | 2 +- rpc/sip.psrpc.go | 2 +- rpc/whip_signal.psrpc.go | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/rpc/agent.psrpc.go b/rpc/agent.psrpc.go index 3b61aab6a..434d66530 100644 --- a/rpc/agent.psrpc.go +++ b/rpc/agent.psrpc.go @@ -1,4 +1,4 @@ -// Code generated by protoc-gen-psrpc v0.7.0, DO NOT EDIT. +// Code generated by protoc-gen-psrpc v0.7.2, DO NOT EDIT. // source: rpc/agent.proto package rpc diff --git a/rpc/agent_dispatch.psrpc.go b/rpc/agent_dispatch.psrpc.go index c28eb3cd1..29d546646 100644 --- a/rpc/agent_dispatch.psrpc.go +++ b/rpc/agent_dispatch.psrpc.go @@ -1,4 +1,4 @@ -// Code generated by protoc-gen-psrpc v0.7.0, DO NOT EDIT. +// Code generated by protoc-gen-psrpc v0.7.2, DO NOT EDIT. // source: rpc/agent_dispatch.proto package rpc diff --git a/rpc/egress.psrpc.go b/rpc/egress.psrpc.go index 9481f9753..2cd187b02 100644 --- a/rpc/egress.psrpc.go +++ b/rpc/egress.psrpc.go @@ -1,4 +1,4 @@ -// Code generated by protoc-gen-psrpc v0.7.0, DO NOT EDIT. +// Code generated by protoc-gen-psrpc v0.7.2, DO NOT EDIT. // source: rpc/egress.proto package rpc diff --git a/rpc/ingress.psrpc.go b/rpc/ingress.psrpc.go index 8e1f48bec..25faf9c1d 100644 --- a/rpc/ingress.psrpc.go +++ b/rpc/ingress.psrpc.go @@ -1,4 +1,4 @@ -// Code generated by protoc-gen-psrpc v0.7.0, DO NOT EDIT. +// Code generated by protoc-gen-psrpc v0.7.2, DO NOT EDIT. // source: rpc/ingress.proto package rpc diff --git a/rpc/io.psrpc.go b/rpc/io.psrpc.go index fb1bce3c0..dc6c6f9a6 100644 --- a/rpc/io.psrpc.go +++ b/rpc/io.psrpc.go @@ -1,4 +1,4 @@ -// Code generated by protoc-gen-psrpc v0.7.0, DO NOT EDIT. +// Code generated by protoc-gen-psrpc v0.7.2, DO NOT EDIT. // source: rpc/io.proto package rpc diff --git a/rpc/keepalive.psrpc.go b/rpc/keepalive.psrpc.go index a67dc376e..4735f8d58 100644 --- a/rpc/keepalive.psrpc.go +++ b/rpc/keepalive.psrpc.go @@ -1,4 +1,4 @@ -// Code generated by protoc-gen-psrpc v0.7.0, DO NOT EDIT. +// Code generated by protoc-gen-psrpc v0.7.2, DO NOT EDIT. // source: rpc/keepalive.proto package rpc diff --git a/rpc/participant.psrpc.go b/rpc/participant.psrpc.go index 12a77fa87..5a3fd5e7a 100644 --- a/rpc/participant.psrpc.go +++ b/rpc/participant.psrpc.go @@ -1,4 +1,4 @@ -// Code generated by protoc-gen-psrpc v0.7.0, DO NOT EDIT. +// Code generated by protoc-gen-psrpc v0.7.2, DO NOT EDIT. // source: rpc/participant.proto package rpc diff --git a/rpc/room.psrpc.go b/rpc/room.psrpc.go index 155c33c78..0b1a8b622 100644 --- a/rpc/room.psrpc.go +++ b/rpc/room.psrpc.go @@ -1,4 +1,4 @@ -// Code generated by protoc-gen-psrpc v0.7.0, DO NOT EDIT. +// Code generated by protoc-gen-psrpc v0.7.2, DO NOT EDIT. // source: rpc/room.proto package rpc diff --git a/rpc/roommanager.psrpc.go b/rpc/roommanager.psrpc.go index 31b6cb7e0..9dbc22974 100644 --- a/rpc/roommanager.psrpc.go +++ b/rpc/roommanager.psrpc.go @@ -1,4 +1,4 @@ -// Code generated by protoc-gen-psrpc v0.7.0, DO NOT EDIT. +// Code generated by protoc-gen-psrpc v0.7.2, DO NOT EDIT. // source: rpc/roommanager.proto package rpc diff --git a/rpc/signal.psrpc.go b/rpc/signal.psrpc.go index a2d80b7d0..acd68bb05 100644 --- a/rpc/signal.psrpc.go +++ b/rpc/signal.psrpc.go @@ -1,4 +1,4 @@ -// Code generated by protoc-gen-psrpc v0.7.0, DO NOT EDIT. +// Code generated by protoc-gen-psrpc v0.7.2, DO NOT EDIT. // source: rpc/signal.proto package rpc diff --git a/rpc/sip.psrpc.go b/rpc/sip.psrpc.go index 21a4634c3..693696962 100644 --- a/rpc/sip.psrpc.go +++ b/rpc/sip.psrpc.go @@ -1,4 +1,4 @@ -// Code generated by protoc-gen-psrpc v0.7.0, DO NOT EDIT. +// Code generated by protoc-gen-psrpc v0.7.2, DO NOT EDIT. // source: rpc/sip.proto package rpc diff --git a/rpc/whip_signal.psrpc.go b/rpc/whip_signal.psrpc.go index c774f38ce..29707b064 100644 --- a/rpc/whip_signal.psrpc.go +++ b/rpc/whip_signal.psrpc.go @@ -1,4 +1,4 @@ -// Code generated by protoc-gen-psrpc v0.7.0, DO NOT EDIT. +// Code generated by protoc-gen-psrpc v0.7.2, DO NOT EDIT. // source: rpc/whip_signal.proto package rpc