From 4e99a20cc7255a1cef181ef95c3ba3bb1071acbc Mon Sep 17 00:00:00 2001 From: Adam Fisk Date: Fri, 20 Feb 2026 09:58:02 -0700 Subject: [PATCH 01/14] Wire BanditURLOverrides through to MutableURLTest Pass per-proxy callback URLs from config response through to sing-box MutableURLTest URLOverrides, enabling per-ISP bandit proxy assignment on the client side. Co-Authored-By: Claude Opus 4.6 --- go.mod | 70 +++++++-------- go.sum | 206 ++++++++++++++++++--------------------------- vpn/boxoptions.go | 21 ++--- vpn/tunnel_test.go | 4 +- vpn/vpn.go | 2 +- 5 files changed, 133 insertions(+), 170 deletions(-) diff --git a/go.mod b/go.mod index 003c39e5..ef1952db 100644 --- a/go.mod +++ b/go.mod @@ -26,12 +26,12 @@ require ( github.com/alitto/pond v1.9.2 github.com/getlantern/amp v0.0.0-20260113204224-600f8e8dfe5f github.com/getlantern/appdir v0.0.0-20250324200952-507a0625eb01 - github.com/getlantern/common v1.2.1-0.20260121160752-d8ee5791108f + github.com/getlantern/common v1.2.1-0.20260220164942-f4c071c02fcb github.com/getlantern/dnstt v0.0.0-20250530230749-4d64f4edcf0f github.com/getlantern/fronted v0.0.0-20260121001528-92134131dcd2 github.com/getlantern/keepcurrent v0.0.0-20240126172110-2e0264ca385d github.com/getlantern/kindling v0.0.0-20260105215242-8df765e82461 - github.com/getlantern/lantern-box v0.0.6-0.20260203175211-21a1481c709c + github.com/getlantern/lantern-box v0.0.6-0.20260220165004-bff0fac7d776 github.com/getlantern/pluriconfig v0.0.0-20251126214241-8cc8bc561535 github.com/getlantern/timezone v0.0.0-20210901200113-3f9de9d360c9 github.com/go-resty/resty/v2 v2.16.5 @@ -44,17 +44,16 @@ require ( github.com/knadh/koanf/v2 v2.3.0 github.com/r3labs/sse/v2 v2.10.0 github.com/sagernet/sing v0.7.18 - github.com/sagernet/sing-box v1.12.13 - github.com/sagernet/sing-dns v0.4.6 + github.com/sagernet/sing-box v1.12.22 github.com/stretchr/testify v1.11.1 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 - go.opentelemetry.io/otel/sdk v1.39.0 - go.opentelemetry.io/otel/sdk/metric v1.39.0 + go.opentelemetry.io/otel/sdk v1.40.0 + go.opentelemetry.io/otel/sdk/metric v1.40.0 go.uber.org/mock v0.5.0 golang.zx2c4.com/wireguard/wgctrl v0.0.0-20241231184526-a9ab2273dd10 - google.golang.org/protobuf v1.36.10 + google.golang.org/protobuf v1.36.11 gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) @@ -110,6 +109,7 @@ require ( github.com/getlantern/errors v1.0.4 // indirect github.com/getlantern/golog v0.0.0-20230503153817-8e72de7e0a65 // indirect github.com/getlantern/lantern-water v0.0.0-20260130212632-d5ea08838250 // indirect + github.com/getlantern/samizdat v0.0.2 // indirect github.com/getlantern/tlsdialer/v3 v3.0.6-0.20260105215053-2a1cd54af4d5 // indirect github.com/go-json-experiment/json v0.0.0-20250103232110-6a9a0fde9288 // indirect github.com/go-llsqlite/adapter v0.0.0-20230927005056-7f5ce7f0c916 // indirect @@ -123,7 +123,7 @@ require ( github.com/gorilla/csrf v1.7.3-0.20250123201450-9dd6af1f6d30 // indirect github.com/gorilla/securecookie v1.1.2 // indirect github.com/gorilla/websocket v1.5.3 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hdevalence/ed25519consensus v0.2.0 // indirect github.com/huandu/xstrings v1.3.2 // indirect @@ -146,25 +146,24 @@ require ( github.com/nwaples/rardecode v1.1.2 // indirect github.com/pion/datachannel v1.5.10 // indirect github.com/pion/dtls/v2 v2.2.7 // indirect - github.com/pion/ice/v2 v2.2.6 // indirect + github.com/pion/ice/v2 v2.3.24 // indirect github.com/pion/interceptor v0.1.37 // indirect github.com/pion/logging v0.2.3 // indirect - github.com/pion/mdns v0.0.5 // indirect + github.com/pion/mdns v0.0.12 // indirect github.com/pion/randutil v0.1.0 // indirect github.com/pion/rtcp v1.2.15 // indirect github.com/pion/rtp v1.8.12 // indirect github.com/pion/sctp v1.8.37 // indirect github.com/pion/sdp/v3 v3.0.11 // indirect - github.com/pion/srtp/v2 v2.0.9 // indirect + github.com/pion/srtp/v2 v2.0.18 // indirect github.com/pion/stun v0.6.1 // indirect - github.com/pion/transport v0.13.1 // indirect - github.com/pion/transport/v2 v2.2.3 // indirect + github.com/pion/transport/v2 v2.2.4 // indirect github.com/pion/transport/v3 v3.0.7 // indirect - github.com/pion/turn/v2 v2.0.8 // indirect - github.com/pion/webrtc/v3 v3.1.42 // indirect + github.com/pion/turn/v2 v2.1.3 // indirect + github.com/pion/webrtc/v3 v3.2.40 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus-community/pro-bing v0.4.0 // indirect - github.com/refraction-networking/utls v1.7.1 // indirect + github.com/refraction-networking/utls v1.8.2 // indirect github.com/refraction-networking/water v0.7.1-alpha // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417 // indirect @@ -180,7 +179,7 @@ require ( github.com/tailscale/web-client-prebuilt v0.0.0-20250124233751-d4cd19a26976 // indirect github.com/templexxx/cpu v0.1.1 // indirect github.com/templexxx/xorsimd v0.4.3 // indirect - github.com/tetratelabs/wazero v1.7.3 // indirect + github.com/tetratelabs/wazero v1.11.0 // indirect github.com/tevino/abool/v2 v2.1.0 // indirect github.com/tidwall/btree v1.6.0 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect @@ -193,13 +192,15 @@ require ( gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2 v2.11.0 // indirect go.etcd.io/bbolt v1.3.6 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/proto/otlp v1.7.1 // indirect + go.opentelemetry.io/proto/otlp v1.9.0 // indirect go.uber.org/zap/exp v0.3.0 // indirect go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect - golang.org/x/term v0.37.0 // indirect - golang.org/x/text v0.31.0 // indirect + golang.getoutline.org/sdk v0.0.21 // indirect + golang.getoutline.org/sdk/x v0.1.0 // indirect + golang.org/x/term v0.39.0 // indirect + golang.org/x/text v0.33.0 // indirect golang.zx2c4.com/wireguard/windows v0.5.3 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20251124214823-79d6a2a48846 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect modernc.org/libc v1.22.3 // indirect modernc.org/mathutil v1.5.0 // indirect @@ -212,9 +213,8 @@ require ( require ( github.com/Xuanwo/go-locale v1.1.3 github.com/ajg/form v1.5.1 // indirect - github.com/andybalholm/brotli v1.1.0 // indirect + github.com/andybalholm/brotli v1.1.1 // indirect github.com/caddyserver/certmagic v0.23.0 // indirect - github.com/cloudflare/circl v1.6.0 // indirect github.com/cretz/bine v0.2.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/fsnotify/fsnotify v1.9.0 @@ -263,11 +263,11 @@ require ( github.com/sagernet/nftables v0.3.0-beta.4 // indirect github.com/sagernet/quic-go v0.52.0-sing-box-mod.3 // indirect github.com/sagernet/sing-mux v0.3.4 // indirect - github.com/sagernet/sing-quic v0.5.2 // indirect + github.com/sagernet/sing-quic v0.5.3 // indirect github.com/sagernet/sing-shadowsocks v0.2.8 // indirect github.com/sagernet/sing-shadowsocks2 v0.2.1 // indirect github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 // indirect - github.com/sagernet/sing-tun v0.7.10 // indirect + github.com/sagernet/sing-tun v0.7.11 // indirect github.com/sagernet/sing-vmess v0.2.7 // indirect github.com/sagernet/smux v1.5.50-sing-box-mod.1 // indirect github.com/sagernet/wireguard-go v0.0.1-beta.7 // indirect @@ -276,24 +276,24 @@ require ( github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect github.com/vishvananda/netns v0.0.5 // indirect github.com/zeebo/blake3 v0.2.4 // indirect - go.opentelemetry.io/otel v1.39.0 + go.opentelemetry.io/otel v1.40.0 go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.37.0 - go.opentelemetry.io/otel/metric v1.39.0 - go.opentelemetry.io/otel/trace v1.39.0 + go.opentelemetry.io/otel/metric v1.40.0 + go.opentelemetry.io/otel/trace v1.40.0 go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect - golang.org/x/crypto v0.45.0 + golang.org/x/crypto v0.47.0 golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 // indirect - golang.org/x/mod v0.29.0 // indirect - golang.org/x/net v0.47.0 // indirect - golang.org/x/sync v0.18.0 // indirect + golang.org/x/mod v0.31.0 // indirect + golang.org/x/net v0.49.0 // indirect + golang.org/x/sync v0.19.0 // indirect golang.org/x/sys v0.40.0 golang.org/x/time v0.9.0 // indirect - golang.org/x/tools v0.38.0 // indirect + golang.org/x/tools v0.40.0 // indirect golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba // indirect - google.golang.org/grpc v1.75.0 + google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect + google.golang.org/grpc v1.78.0 gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index ae63ee18..c6ef719b 100644 --- a/go.sum +++ b/go.sum @@ -99,8 +99,8 @@ github.com/anacrolix/upnp v0.1.3-0.20220123035249-922794e51c96/go.mod h1:Wa6n8cY github.com/anacrolix/utp v0.1.0 h1:FOpQOmIwYsnENnz7tAGohA+r6iXpRjrq8ssKSre2Cp4= github.com/anacrolix/utp v0.1.0/go.mod h1:MDwc+vsGEq7RMw6lr2GKOEqjWny5hO5OZXRVNaBJ2Dk= github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= -github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= -github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= +github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= github.com/anytls/sing-anytls v0.0.11 h1:w8e9Uj1oP3m4zxkyZDewPk0EcQbvVxb7Nn+rapEx4fc= github.com/anytls/sing-anytls v0.0.11/go.mod h1:7rjN6IukwysmdusYsrV51Fgu1uW6vsrdd6ctjnEAln8= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= @@ -137,8 +137,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cilium/ebpf v0.15.0 h1:7NxJhNiBT3NG8pZJ3c+yfrVdHY8ScgKD27sScgjLMMk= github.com/cilium/ebpf v0.15.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk= -github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE= github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= @@ -180,7 +178,6 @@ github.com/frankban/quicktest v1.9.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9 github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 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/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= @@ -195,8 +192,8 @@ github.com/getlantern/amp v0.0.0-20260113204224-600f8e8dfe5f h1:NLGftemDrbGf7Wce github.com/getlantern/amp v0.0.0-20260113204224-600f8e8dfe5f/go.mod h1:qnMv9szb8JK3kA9W4N2FlYUMj1GkA0x7QEUEPD7tk4o= github.com/getlantern/appdir v0.0.0-20250324200952-507a0625eb01 h1:Mmeh4/DA1OKN9tVWRAvTL5efFx4c7v9/55hoK17NclA= github.com/getlantern/appdir v0.0.0-20250324200952-507a0625eb01/go.mod h1:3vR6+jQdWfWojZ77w+htCqEF5MO/Y2twJOpAvFuM9po= -github.com/getlantern/common v1.2.1-0.20260121160752-d8ee5791108f h1:EqRKCaOBuvVkFsIjeWUYluE4s4TZtVQSClfIWFqcSks= -github.com/getlantern/common v1.2.1-0.20260121160752-d8ee5791108f/go.mod h1:eSSuV4bMPgQJnczBw+KWWqWNo1itzmVxC++qUBPRTt0= +github.com/getlantern/common v1.2.1-0.20260220164942-f4c071c02fcb h1:zTk63u9gyCXfraRjheUJm+wXh+RNaMLfReE4voNRp38= +github.com/getlantern/common v1.2.1-0.20260220164942-f4c071c02fcb/go.mod h1:eSSuV4bMPgQJnczBw+KWWqWNo1itzmVxC++qUBPRTt0= github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY= github.com/getlantern/context v0.0.0-20220418194847-3d5e7a086201 h1:oEZYEpZo28Wdx+5FZo4aU7JFXu0WG/4wJWese5reQSA= github.com/getlantern/context v0.0.0-20220418194847-3d5e7a086201/go.mod h1:Y9WZUHEb+mpra02CbQ/QczLUe6f0Dezxaw5DCJlJQGo= @@ -224,8 +221,8 @@ github.com/getlantern/keepcurrent v0.0.0-20240126172110-2e0264ca385d h1:2/9rPC1x github.com/getlantern/keepcurrent v0.0.0-20240126172110-2e0264ca385d/go.mod h1:enUAvxkJ15QUtTKOKoO9WJV2L5u33P8YmqkC+iu8iT4= github.com/getlantern/kindling v0.0.0-20260105215242-8df765e82461 h1:PwgggQLmIFSjlMr5SgvO0M8jK3XjrJsxLu7IccACs2Y= github.com/getlantern/kindling v0.0.0-20260105215242-8df765e82461/go.mod h1:eg1zov65hEXytiISbE4lcExN3gVhi+ZnPxkoiuVzC6c= -github.com/getlantern/lantern-box v0.0.6-0.20260203175211-21a1481c709c h1:7PeTeyUxUpJJ/H6OgFy+3VLCHdVOLOSzdnmaZoGwn4E= -github.com/getlantern/lantern-box v0.0.6-0.20260203175211-21a1481c709c/go.mod h1:NSY2/Jks8B+USqz4BWbmcdRygAzYYBuL9AAleutKx8k= +github.com/getlantern/lantern-box v0.0.6-0.20260220165004-bff0fac7d776 h1:Z9515MBli04kLtymOIy0Z312N/L5heUQ772d1Ttc+JM= +github.com/getlantern/lantern-box v0.0.6-0.20260220165004-bff0fac7d776/go.mod h1:x4kOCOi7SdaWFfod6LpvfEwkzZCbeJzWQcoNsY7yeFk= github.com/getlantern/lantern-water v0.0.0-20260130212632-d5ea08838250 h1:xculJyC6hS0kNSQKWBP1FQbpSVmeJyhUGID804jgKCA= github.com/getlantern/lantern-water v0.0.0-20260130212632-d5ea08838250/go.mod h1:ZpSOrcdJkmb8MvaQn6mxaidxshlyi+RJLUerhW4L5Lo= github.com/getlantern/mockconn v0.0.0-20200818071412-cb30d065a848 h1:2MhMMVBTnaHrst6HyWFDhwQCaJ05PZuOv1bE2gN8WFY= @@ -242,6 +239,8 @@ github.com/getlantern/osversion v0.0.0-20240418205916-2e84a4a4e175 h1:JWH5BB2o0e github.com/getlantern/osversion v0.0.0-20240418205916-2e84a4a4e175/go.mod h1:h3S9LBmmzN/xM+lwYZHE4abzTtCTtidKtG+nxZcCZX0= github.com/getlantern/pluriconfig v0.0.0-20251126214241-8cc8bc561535 h1:rtDmW8YLAuT8r51ApR5z0d8/qjhHu3TW+divQ2C98Ac= github.com/getlantern/pluriconfig v0.0.0-20251126214241-8cc8bc561535/go.mod h1:WKJEdjMOD4IuTRYwjQHjT4bmqDl5J82RShMLxPAvi0Q= +github.com/getlantern/samizdat v0.0.2 h1:PkMu6jsfUz7DLZUH2xh548XfzgPASmq5CajZyUKj/9Y= +github.com/getlantern/samizdat v0.0.2/go.mod h1:uEeykQSW2/6rTjfPlj3MTTo59poSHXfAHTGgzYDkbr0= github.com/getlantern/sing v0.7.18-lantern h1:QKGgIUA3LwmKYP/7JlQTRkxj9jnP4cX2Q/B+nd8XEjo= github.com/getlantern/sing v0.7.18-lantern/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/getlantern/sing-box-minimal v1.12.19-lantern h1:Tntq+Udsvyv6A/mjxfSoZ8NhvhXRSX6i/CICKGPFhAY= @@ -293,7 +292,6 @@ github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= @@ -328,8 +326,6 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -356,7 +352,7 @@ github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806 h1:wG8RYIyctLhdFk6Vl1yPGtSRtwGpVkWyZww1OCil2MI= github.com/google/nftables v0.2.1-0.20240414091927-5e242ec57806/go.mod h1:Beg6V6zZ3oEn0JuiUQ4wqwuyqqzasOltcoXPtgLbFp4= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -371,8 +367,8 @@ github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kX github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= 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= @@ -489,17 +485,9 @@ github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/nwaples/rardecode v1.1.2 h1:Cj0yZY6T1Zx1R7AhTbyGSALm44/Mmq+BAPc4B/p/d3M= github.com/nwaples/rardecode v1.1.2/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= @@ -510,58 +498,47 @@ github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= -github.com/pion/datachannel v1.5.2/go.mod h1:FTGQWaHrdCwIJ1rw6xBIfZVkslikjShim5yr05XFuCQ= 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/v2 v2.1.3/go.mod h1:o6+WvyLDAlXF7YiPB/RlskRoeK+/JtuaZa5emwQcWus= -github.com/pion/dtls/v2 v2.1.5/go.mod h1:BqCE7xPZbPSubGasRoDFJeTsyJtdD1FanJYL0JGheqY= github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8= github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= -github.com/pion/ice/v2 v2.2.6 h1:R/vaLlI1J2gCx141L5PEwtuGAGcyS6e7E0hDeJFq5Ig= -github.com/pion/ice/v2 v2.2.6/go.mod h1:SWuHiOGP17lGromHTFadUe1EuPgFh/oCU6FCMZHooVE= -github.com/pion/interceptor v0.1.11/go.mod h1:tbtKjZY14awXd7Bq0mmWvgtHB5MDaRN7HV3OZ/uy7s8= +github.com/pion/ice/v2 v2.3.24 h1:RYgzhH/u5lH0XO+ABatVKCtRd+4U1GEaCXSMjNr13tI= +github.com/pion/ice/v2 v2.3.24/go.mod h1:KXJJcZK7E8WzrBEYnV4UtqEZsGeWfHxsNqhVcVvgjxw= github.com/pion/interceptor v0.1.37 h1:aRA8Zpab/wE7/c0O3fh1PqY0AJI3fCSEM5lRWJVorwI= github.com/pion/interceptor v0.1.37/go.mod h1:JzxbJ4umVTlZAf+/utHzNesY8tmRkM2lVmkS82TTj8Y= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI= github.com/pion/logging v0.2.3/go.mod h1:z8YfknkquMe1csOrxK5kc+5/ZPAzMxbKLX5aXpbpC90= -github.com/pion/mdns v0.0.5 h1:Q2oj/JB3NqfzY9xGZ1fPzZzK7sDSD8rZPOvcIQ10BCw= -github.com/pion/mdns v0.0.5/go.mod h1:UgssrvdD3mxpi8tMxAXbsppL3vJ4Jipw1mTCW+al01g= +github.com/pion/mdns v0.0.12 h1:CiMYlY+O0azojWDmxdNr7ADGrnZ+V6Ilfner+6mSVK8= +github.com/pion/mdns v0.0.12/go.mod h1:VExJjv8to/6Wqm1FXK+Ii/Z9tsVk/F5sD/N70cnYFbk= 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.9/go.mod h1:qVPhiCzAm4D/rxb6XzKeyZiQK69yJpbUDJSF7TgrqNo= +github.com/pion/rtcp v1.2.12/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4= 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.7.13/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= +github.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= github.com/pion/rtp v1.8.12 h1:nsKs8Wi0jQyBFHU3qmn/OvtZrhktVfJY0vRxwACsL5U= github.com/pion/rtp v1.8.12/go.mod h1:8uMBJj32Pa1wwx8Fuv/AsFhn8jsgw+3rUC2PfoBZ8p4= -github.com/pion/sctp v1.8.0/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s= -github.com/pion/sctp v1.8.2/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s= github.com/pion/sctp v1.8.37 h1:ZDmGPtRPX9mKCiVXtMbTWybFw3z/hVKAZgU81wcOrqs= github.com/pion/sctp v1.8.37/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE= -github.com/pion/sdp/v3 v3.0.5/go.mod h1:iiFWFpQO8Fy3S5ldclBkpXqmWy02ns78NOKoLLL0YQw= github.com/pion/sdp/v3 v3.0.11 h1:VhgVSopdsBKwhCFoyyPmT1fKMeV9nLMrEKxNOdy3IVI= github.com/pion/sdp/v3 v3.0.11/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E= -github.com/pion/srtp/v2 v2.0.9 h1:JJq3jClmDFBPX/F5roEb0U19jSU7eUhyDqR/NZ34EKQ= -github.com/pion/srtp/v2 v2.0.9/go.mod h1:5TtM9yw6lsH0ppNCehB/EjEUli7VkUgKSPJqWVqbhQ4= -github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2UA= +github.com/pion/srtp/v2 v2.0.18 h1:vKpAXfawO9RtTRKZJbG4y0v1b11NZxQnxRl85kGuUlo= +github.com/pion/srtp/v2 v2.0.18/go.mod h1:0KJQjA99A6/a0DOVTu1PhDSw0CXF2jTkqOoMg3ODqdA= github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4= github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8= -github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q= -github.com/pion/transport v0.12.3/go.mod h1:OViWW9SP2peE/HbwBvARicmAVnesphkNkCVZIWJ6q9A= -github.com/pion/transport v0.13.0/go.mod h1:yxm9uXpK9bpBBWkITk13cLo1y5/ur5VQpG22ny6EP7g= -github.com/pion/transport v0.13.1 h1:/UH5yLeQtwm2VZIPjxwnNFxjS4DFhyLfS4GlfuKUzfA= -github.com/pion/transport v0.13.1/go.mod h1:EBxbqzyv+ZrmDb82XswEE0BjfQFtuw1Nu6sjnjWCsGg= github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= -github.com/pion/transport/v2 v2.2.3 h1:XcOE3/x41HOSKbl1BfyY1TF1dERx7lVvlMCbXU7kfvA= +github.com/pion/transport/v2 v2.2.2/go.mod h1:OJg3ojoBJopjEeECq2yJdXH9YVrUJ1uQ++NjXLOUorc= github.com/pion/transport/v2 v2.2.3/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= +github.com/pion/transport/v2 v2.2.4 h1:41JJK6DZQYSeVLxILA2+F4ZkKb4Xd/tFJZRFZQ9QAlo= +github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= +github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= 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/v2 v2.0.8 h1:KEstL92OUN3k5k8qxsXHpr7WWfrdp7iJZHx99ud8muw= -github.com/pion/turn/v2 v2.0.8/go.mod h1:+y7xl719J8bAEVpSXBXvTxStjJv3hbz9YFflvkpcGPw= -github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M= -github.com/pion/webrtc/v3 v3.1.42 h1:wJEQFIXVanptnQcHOLTuIo4AtGB2+mG2x4OhIhnITOA= -github.com/pion/webrtc/v3 v3.1.42/go.mod h1:ffD9DulDrPxyWvDPUIPAOSAWx9GUlOExiJPf7cCcMLA= +github.com/pion/turn/v2 v2.1.3 h1:pYxTVWG2gpC97opdRc5IGsQ1lJ9O/IlNhkzj7MMrGAA= +github.com/pion/turn/v2 v2.1.3/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY= +github.com/pion/webrtc/v3 v3.2.40 h1:Wtfi6AZMQg+624cvCXUuSmrKWepSB7zfgYDOYqsSOVU= +github.com/pion/webrtc/v3 v3.2.40/go.mod h1:M1RAe3TNTD1tzyvqHrbVODfwdPGSXOUo/OgpoGGJqFY= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -594,8 +571,8 @@ github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1 github.com/r3labs/sse/v2 v2.10.0 h1:hFEkLLFY4LDifoHdiCN/LlGBAdVJYsANaLqNYa1l/v0= github.com/r3labs/sse/v2 v2.10.0/go.mod h1:Igau6Whc+F17QUgML1fYe1VPZzTV6EMCnYktEmkNJ7I= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/refraction-networking/utls v1.7.1 h1:dxg+jla3uocgN8HtX+ccwDr68uCBBO3qLrkZUbqkcw0= -github.com/refraction-networking/utls v1.7.1/go.mod h1:TUhh27RHMGtQvjQq+RyO11P6ZNQNBb3N0v7wsEjKAIQ= +github.com/refraction-networking/utls v1.8.2 h1:j4Q1gJj0xngdeH+Ox/qND11aEfhpgoEvV+S9iJ2IdQo= +github.com/refraction-networking/utls v1.8.2/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM= github.com/refraction-networking/water v0.7.1-alpha h1:Q7AVr9qx7vUNhJYK1F96DIweDPZ4e5IdnRN/OpHhGUo= github.com/refraction-networking/water v0.7.1-alpha/go.mod h1:/Es8MEj+895tQhx6Sl09It+Hmk7eC4tuPbxSvgsBd2c= github.com/refraction-networking/wazero v1.7.1-w h1:z7Ty5PsMkJEDBCsn3ELUjceQGBT0FMVGldOSpDK3giQ= @@ -629,20 +606,18 @@ github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNen github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8= github.com/sagernet/quic-go v0.52.0-sing-box-mod.3 h1:ySqffGm82rPqI1TUPqmtHIYd12pfEGScygnOxjTL56w= github.com/sagernet/quic-go v0.52.0-sing-box-mod.3/go.mod h1:OV+V5kEBb8kJS7k29MzDu6oj9GyMc7HA07sE1tedxz4= -github.com/sagernet/sing-dns v0.4.6 h1:mjZC0o6d5sQ1sraoOBbK3G3apCbuL8wWYwu2RNu5rbM= -github.com/sagernet/sing-dns v0.4.6/go.mod h1:dweQs54ng2YGzoJfz+F9dGuDNdP5pJ3PLeggnK5VWc8= github.com/sagernet/sing-mux v0.3.4 h1:ZQplKl8MNXutjzbMVtWvWG31fohhgOfCuUZR4dVQ8+s= github.com/sagernet/sing-mux v0.3.4/go.mod h1:QvlKMyNBNrQoyX4x+gq028uPbLM2XeRpWtDsWBJbFSk= -github.com/sagernet/sing-quic v0.5.2 h1:I3vlfRImhr0uLwRS3b3ib70RMG9FcXtOKKUDz3eKRWc= -github.com/sagernet/sing-quic v0.5.2/go.mod h1:evP1e++ZG8TJHVV5HudXV4vWeYzGfCdF4HwSJZcdqkI= +github.com/sagernet/sing-quic v0.5.3 h1:K937DKJN98xqyztijRkLJqbBfyV4rEZcYxFyP3EBikU= +github.com/sagernet/sing-quic v0.5.3/go.mod h1:evP1e++ZG8TJHVV5HudXV4vWeYzGfCdF4HwSJZcdqkI= github.com/sagernet/sing-shadowsocks v0.2.8 h1:PURj5PRoAkqeHh2ZW205RWzN9E9RtKCVCzByXruQWfE= github.com/sagernet/sing-shadowsocks v0.2.8/go.mod h1:lo7TWEMDcN5/h5B8S0ew+r78ZODn6SwVaFhvB6H+PTI= github.com/sagernet/sing-shadowsocks2 v0.2.1 h1:dWV9OXCeFPuYGHb6IRqlSptVnSzOelnqqs2gQ2/Qioo= github.com/sagernet/sing-shadowsocks2 v0.2.1/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ= github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11 h1:tK+75l64tm9WvEFrYRE1t0YxoFdWQqw/h7Uhzj0vJ+w= github.com/sagernet/sing-shadowtls v0.2.1-0.20250503051639-fcd445d33c11/go.mod h1:sWqKnGlMipCHaGsw1sTTlimyUpgzP4WP3pjhCsYt9oA= -github.com/sagernet/sing-tun v0.7.10 h1:lLBaS9uL0mK/FCGDe3N4oKQxjMGfmv3u2/6jKtmq4Pw= -github.com/sagernet/sing-tun v0.7.10/go.mod h1:pUEjh9YHQ2gJT6Lk0TYDklh3WJy7lz+848vleGM3JPM= +github.com/sagernet/sing-tun v0.7.11 h1:qB7jy8JKqXg73fYBsDkBSy4ulRSbLrFut0e+y+QPhqU= +github.com/sagernet/sing-tun v0.7.11/go.mod h1:pUEjh9YHQ2gJT6Lk0TYDklh3WJy7lz+848vleGM3JPM= github.com/sagernet/sing-vmess v0.2.7 h1:2ee+9kO0xW5P4mfe6TYVWf9VtY8k1JhNysBqsiYj0sk= github.com/sagernet/sing-vmess v0.2.7/go.mod h1:5aYoOtYksAyS0NXDm0qKeTYW1yoE1bJVcv+XLcVoyJs= github.com/sagernet/smux v1.5.50-sing-box-mod.1 h1:XkJcivBC9V4wBjiGXIXZ229aZCU1hzcbp6kSkkyQ478= @@ -651,7 +626,6 @@ github.com/sagernet/tailscale v1.80.3-sing-box-1.12-mod.2 h1:MO7s4ni2bSfAOhcan2r github.com/sagernet/tailscale v1.80.3-sing-box-1.12-mod.2/go.mod h1:EBxXsWu4OH2ELbQLq32WoBeIubG8KgDrg4/Oaxjs6lI= github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 h1:6uUiZcDRnZSAegryaUGwPC/Fj13JSHwiTftrXhMmYOc= github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854/go.mod h1:LtfoSK3+NG57tvnVEHgcuBW9ujgE8enPSgzgwStwCAA= -github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/shadowsocks/go-shadowsocks2 v0.1.5 h1:PDSQv9y2S85Fl7VBeOMF9StzeXZyK1HakRm86CUbr28= github.com/shadowsocks/go-shadowsocks2 v0.1.5/go.mod h1:AGGpIoek4HRno4xzyFiAtLHkOpcoznZEkAccaI/rplM= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -735,7 +709,8 @@ github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+A github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE= github.com/xtaci/smux v1.5.34 h1:OUA9JaDFHJDT8ZT3ebwLWPAgEfE6sWo2LaTy3anXqwg= github.com/xtaci/smux v1.5.34/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= +github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= @@ -756,25 +731,25 @@ go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbE go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0/go.mod h1:NfchwuyNoMcZ5MLHwPrODwUF1HWCXWrL31s8gSAdIKY= go.opentelemetry.io/otel v1.9.0/go.mod h1:np4EoPGzoPs3O67xUVNoPPcmSvsfOxNlNA4F4AC+0Eo= -go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= -go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= +go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.37.0 h1:zG8GlgXCJQd5BU98C0hZnBbElszTmUgCNCfYneaDL0A= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.37.0/go.mod h1:hOfBCz8kv/wuq73Mx2H2QnWokh/kHZxkh6SNF2bdKtw= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 h1:EtFWSnwW9hGObjkIdmlnWSydO+Qs8OwzfzXLUPg4xOc= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0/go.mod h1:QjUEoiGCPkvFZ/MjK6ZZfNOS6mfVEVKYE99dFhuN2LI= -go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= -go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= -go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= -go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= -go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= -go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g= +go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc= +go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8= +go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE= +go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw= +go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg= go.opentelemetry.io/otel/trace v1.9.0/go.mod h1:2737Q0MuG8q1uILYm2YYVkAyLtOofiTNGg6VODnOiPo= -go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= -go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= -go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= -go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= +go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw= +go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= +go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= +go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -793,6 +768,10 @@ go4.org/mem v0.0.0-20240501181205-ae6ca9944745 h1:Tl++JLUCe4sxGu8cTpDzRLd3tN7US4 go4.org/mem v0.0.0-20240501181205-ae6ca9944745/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y= +golang.getoutline.org/sdk v0.0.21 h1:zgtenz5DMbnIPOsuAOHNiWdrri81fHyBxhSfRi6Dk8s= +golang.getoutline.org/sdk v0.0.21/go.mod h1:raUAs4PYbEaT/cLTK6PviiKSh7gjEj7JJczFFFr41zc= +golang.getoutline.org/sdk/x v0.1.0 h1:8ykaCEC8Eoi3h/2MdGW7uaMAt2BWFCRhrSvuJ0Y/IU0= +golang.getoutline.org/sdk/x v0.1.0/go.mod h1:Vw7FWpLbYifHFYbbo0mXOCkhR14d1ADwjiF7uBQKyzM= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -802,14 +781,13 @@ golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220516162934-403b01795ae8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= +golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI= golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ= @@ -820,12 +798,11 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= -golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= +golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= +golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -838,29 +815,20 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191116160921-f9c825593386/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220401154927-543a649e0bdd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 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.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -869,12 +837,11 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -884,27 +851,20 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/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-20220608164250-635b8c9b7f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -912,8 +872,11 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= @@ -922,10 +885,12 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= +golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -934,10 +899,12 @@ golang.org/x/text v0.3.6/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.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -948,12 +915,11 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 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.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= -golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= +golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -973,28 +939,26 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto/googleapis/api v0.0.0-20251124214823-79d6a2a48846 h1:ZdyUkS9po3H7G0tuh955QVyyotWvOD4W0aEapeGeUYk= -google.golang.org/genproto/googleapis/api v0.0.0-20251124214823-79d6a2a48846/go.mod h1:Fk4kyraUvqD7i5H6S43sj2W98fbZa75lpZz/eUyhfO0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba h1:UKgtfRM7Yh93Sya0Fo8ZzhDP4qBckrrxEr2oF5UIVb8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M= +google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4= -google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= +google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/cenkalti/backoff.v1 v1.1.0 h1:Arh75ttbsvlpVA7WtVpH4u9h6Zl46xuptxqLxPiSo4Y= gopkg.in/cenkalti/backoff.v1 v1.1.0/go.mod h1:J6Vskwqd+OMVJl8C33mmtxTBs2gyzfv7UDAkHu8BrjI= @@ -1013,8 +977,6 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -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.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/vpn/boxoptions.go b/vpn/boxoptions.go index 10b5de5d..39856cb2 100644 --- a/vpn/boxoptions.go +++ b/vpn/boxoptions.go @@ -318,7 +318,7 @@ func buildOptions(ctx context.Context, path string) (O.Options, error) { lanternTags = mergeAndCollectTags(&opts, &configOpts) slog.Debug("Merged config options", "tags", lanternTags) - appendGroupOutbounds(&opts, servers.SGLantern, autoLanternTag, lanternTags) + appendGroupOutbounds(&opts, servers.SGLantern, autoLanternTag, lanternTags, cfg.BanditURLOverrides) // Load user servers slog.Debug("Loading user servers") @@ -334,14 +334,14 @@ func buildOptions(ctx context.Context, path string) (O.Options, error) { userTags = mergeAndCollectTags(&opts, &userOpts) slog.Debug("Merged user server options", "tags", userTags) } - appendGroupOutbounds(&opts, servers.SGUser, autoUserTag, userTags) + appendGroupOutbounds(&opts, servers.SGUser, autoUserTag, userTags, nil) if len(lanternTags) == 0 && len(userTags) == 0 { return O.Options{}, errors.New("no outbounds or endpoints found in config or user servers") } // Add auto all outbound - opts.Outbounds = append(opts.Outbounds, urlTestOutbound(autoAllTag, []string{autoLanternTag, autoUserTag})) + opts.Outbounds = append(opts.Outbounds, urlTestOutbound(autoAllTag, []string{autoLanternTag, autoUserTag}, nil)) // Add routing rules for the groups opts.Route.Rules = append(opts.Route.Rules, groupRule(autoAllTag)) @@ -439,8 +439,8 @@ func useIfNotZero[T comparable](newVal, oldVal T) T { return oldVal } -func appendGroupOutbounds(opts *O.Options, serverGroup, autoTag string, tags []string) { - opts.Outbounds = append(opts.Outbounds, urlTestOutbound(autoTag, tags)) +func appendGroupOutbounds(opts *O.Options, serverGroup, autoTag string, tags []string, urlOverrides map[string]string) { + opts.Outbounds = append(opts.Outbounds, urlTestOutbound(autoTag, tags, urlOverrides)) opts.Outbounds = append(opts.Outbounds, selectorOutbound(serverGroup, append([]string{autoTag}, tags...))) slog.Log( nil, internal.LevelTrace, "Added group outbounds", @@ -463,15 +463,16 @@ func groupAutoTag(group string) string { } } -func urlTestOutbound(tag string, outbounds []string) O.Outbound { +func urlTestOutbound(tag string, outbounds []string, urlOverrides map[string]string) O.Outbound { return O.Outbound{ Type: lbC.TypeMutableURLTest, Tag: tag, Options: &lbO.MutableURLTestOutboundOptions{ - Outbounds: outbounds, - URL: "https://google.com/generate_204", - Interval: badoption.Duration(urlTestInterval), - IdleTimeout: badoption.Duration(urlTestIdleTimeout), + Outbounds: outbounds, + URL: "https://google.com/generate_204", + URLOverrides: urlOverrides, + Interval: badoption.Duration(urlTestInterval), + IdleTimeout: badoption.Duration(urlTestIdleTimeout), }, } } diff --git a/vpn/tunnel_test.go b/vpn/tunnel_test.go index b7940e24..541193ef 100644 --- a/vpn/tunnel_test.go +++ b/vpn/tunnel_test.go @@ -71,10 +71,10 @@ func TestUpdateServers(t *testing.T) { outs := []sbO.Outbound{ allOutbounds["direct"], allOutbounds["block"], allOutbounds["http1-out"], allOutbounds["http2-out"], allOutbounds["socks1-out"], - urlTestOutbound(autoLanternTag, lanternTags), urlTestOutbound(autoUserTag, userTags), + urlTestOutbound(autoLanternTag, lanternTags, nil), urlTestOutbound(autoUserTag, userTags, nil), selectorOutbound(servers.SGLantern, append(lanternTags, autoLanternTag)), selectorOutbound(servers.SGUser, append(userTags, autoUserTag)), - urlTestOutbound(autoAllTag, []string{autoLanternTag, autoUserTag}), + urlTestOutbound(autoAllTag, []string{autoLanternTag, autoUserTag}, nil), } testOpts.Outbounds = outs diff --git a/vpn/vpn.go b/vpn/vpn.go index 5aaed227..3edca3f8 100644 --- a/vpn/vpn.go +++ b/vpn/vpn.go @@ -361,7 +361,7 @@ func preTest(path string) (map[string]uint16, error) { for _, ob := range outbounds { tags = append(tags, ob.Tag) } - outbounds = append(outbounds, urlTestOutbound("preTest", tags)) + outbounds = append(outbounds, urlTestOutbound("preTest", tags, nil)) options := option.Options{ Log: &option.LogOptions{Disabled: true}, Outbounds: outbounds, From bba0b2ec466cd7ea1132a80acdc4cddedc1d49d5 Mon Sep 17 00:00:00 2001 From: Adam Fisk Date: Fri, 20 Feb 2026 11:06:01 -0700 Subject: [PATCH 02/14] Pass BanditURLOverrides to preTest URL tests Forward cfg.BanditURLOverrides into the preTest urlTestOutbound so bandit callback URLs are used during pre-start URL testing, not just during normal operation. Co-Authored-By: Claude Opus 4.6 --- vpn/vpn.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vpn/vpn.go b/vpn/vpn.go index 3edca3f8..fade406b 100644 --- a/vpn/vpn.go +++ b/vpn/vpn.go @@ -361,7 +361,7 @@ func preTest(path string) (map[string]uint16, error) { for _, ob := range outbounds { tags = append(tags, ob.Tag) } - outbounds = append(outbounds, urlTestOutbound("preTest", tags, nil)) + outbounds = append(outbounds, urlTestOutbound("preTest", tags, cfg.BanditURLOverrides)) options := option.Options{ Log: &option.LogOptions{Disabled: true}, Outbounds: outbounds, From 823eb1d57ffe764c0fca0edf5fdabfd1c40ba2e7 Mon Sep 17 00:00:00 2001 From: Adam Fisk Date: Fri, 20 Feb 2026 11:06:05 -0700 Subject: [PATCH 03/14] Add test for BanditURLOverrides wiring in buildOptions Verify that URLOverrides from config.BanditURLOverrides are correctly forwarded into the auto-lantern MutableURLTestOutboundOptions. Co-Authored-By: Claude Opus 4.6 --- vpn/boxoptions_test.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/vpn/boxoptions_test.go b/vpn/boxoptions_test.go index cacd91d1..edcc0571 100644 --- a/vpn/boxoptions_test.go +++ b/vpn/boxoptions_test.go @@ -223,6 +223,36 @@ func TestBuildOptions_Rulesets(t *testing.T) { }) } +func TestBuildOptions_BanditURLOverrides(t *testing.T) { + testOpts, _, err := testBoxOptions("") + require.NoError(t, err) + lanternTags, lanternOuts := filterOutbounds(*testOpts, constant.TypeHTTP) + require.NotEmpty(t, lanternTags, "need at least one HTTP outbound for test") + + overrides := map[string]string{ + lanternTags[0]: "https://example.com/callback?token=abc", + } + cfg := config.Config{ + ConfigResponse: LC.ConfigResponse{ + Options: O.Options{Outbounds: lanternOuts}, + BanditURLOverrides: overrides, + }, + } + + path := t.TempDir() + testOptsToFile(t, cfg, filepath.Join(path, common.ConfigFileName)) + + opts, err := buildOptions(context.Background(), path) + require.NoError(t, err) + + out := findOutbound(opts.Outbounds, autoLanternTag) + require.NotNil(t, out, "auto-lantern outbound not found") + + mutOpts, ok := out.Options.(*lbO.MutableURLTestOutboundOptions) + require.True(t, ok, "auto-lantern outbound should be MutableURLTestOutboundOptions") + assert.Equal(t, overrides, mutOpts.URLOverrides, "URLOverrides should be wired from config") +} + func contains[S ~[]E, E any](t *testing.T, s S, e E) bool { for _, v := range s { if optsEqual(t, v, e) { From 7b3bd80ac2fbcab1ab4f7a863817e7a2e9691cb5 Mon Sep 17 00:00:00 2001 From: Adam Fisk Date: Tue, 24 Feb 2026 12:02:41 -0700 Subject: [PATCH 04/14] Add throughput reporter with jittered timing and padded requests Periodically reports per-outbound throughput to the bandit system so it can optimize for actual download speed, not just connectivity. Interval and body size are randomized to avoid creating a fingerprintable signal. Co-Authored-By: Claude Opus 4.6 --- go.mod | 2 +- go.sum | 4 +- vpn/boxoptions.go | 22 +++-- vpn/boxoptions_test.go | 14 +-- vpn/service.go | 10 ++- vpn/throughput_reporter.go | 174 +++++++++++++++++++++++++++++++++++++ 6 files changed, 209 insertions(+), 17 deletions(-) create mode 100644 vpn/throughput_reporter.go diff --git a/go.mod b/go.mod index ef1952db..caf501f4 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/alitto/pond v1.9.2 github.com/getlantern/amp v0.0.0-20260113204224-600f8e8dfe5f github.com/getlantern/appdir v0.0.0-20250324200952-507a0625eb01 - github.com/getlantern/common v1.2.1-0.20260220164942-f4c071c02fcb + github.com/getlantern/common v1.2.1-0.20260224165050-c96e51281bb8 github.com/getlantern/dnstt v0.0.0-20250530230749-4d64f4edcf0f github.com/getlantern/fronted v0.0.0-20260121001528-92134131dcd2 github.com/getlantern/keepcurrent v0.0.0-20240126172110-2e0264ca385d diff --git a/go.sum b/go.sum index c6ef719b..cc30f184 100644 --- a/go.sum +++ b/go.sum @@ -192,8 +192,8 @@ github.com/getlantern/amp v0.0.0-20260113204224-600f8e8dfe5f h1:NLGftemDrbGf7Wce github.com/getlantern/amp v0.0.0-20260113204224-600f8e8dfe5f/go.mod h1:qnMv9szb8JK3kA9W4N2FlYUMj1GkA0x7QEUEPD7tk4o= github.com/getlantern/appdir v0.0.0-20250324200952-507a0625eb01 h1:Mmeh4/DA1OKN9tVWRAvTL5efFx4c7v9/55hoK17NclA= github.com/getlantern/appdir v0.0.0-20250324200952-507a0625eb01/go.mod h1:3vR6+jQdWfWojZ77w+htCqEF5MO/Y2twJOpAvFuM9po= -github.com/getlantern/common v1.2.1-0.20260220164942-f4c071c02fcb h1:zTk63u9gyCXfraRjheUJm+wXh+RNaMLfReE4voNRp38= -github.com/getlantern/common v1.2.1-0.20260220164942-f4c071c02fcb/go.mod h1:eSSuV4bMPgQJnczBw+KWWqWNo1itzmVxC++qUBPRTt0= +github.com/getlantern/common v1.2.1-0.20260224165050-c96e51281bb8 h1:zFDasLgbFLsXK13+62i3gKePi74q6olfh5mKN2+4BXY= +github.com/getlantern/common v1.2.1-0.20260224165050-c96e51281bb8/go.mod h1:eSSuV4bMPgQJnczBw+KWWqWNo1itzmVxC++qUBPRTt0= github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY= github.com/getlantern/context v0.0.0-20220418194847-3d5e7a086201 h1:oEZYEpZo28Wdx+5FZo4aU7JFXu0WG/4wJWese5reQSA= github.com/getlantern/context v0.0.0-20220418194847-3d5e7a086201/go.mod h1:Y9WZUHEb+mpra02CbQ/QczLUe6f0Dezxaw5DCJlJQGo= diff --git a/vpn/boxoptions.go b/vpn/boxoptions.go index 39856cb2..0a5ce110 100644 --- a/vpn/boxoptions.go +++ b/vpn/boxoptions.go @@ -237,8 +237,15 @@ func baseRoutingRules() []O.Rule { return rules } +// buildOptionsResult bundles the outputs of buildOptions so callers can +// access both the sing-box options and bandit metadata. +type buildOptionsResult struct { + Options O.Options + BanditThroughputURL string +} + // buildOptions builds the box options using the config options and user servers. -func buildOptions(ctx context.Context, path string) (O.Options, error) { +func buildOptions(ctx context.Context, path string) (buildOptionsResult, error) { ctx, span := otel.Tracer(tracerName).Start(ctx, "buildOptions") defer span.End() @@ -260,7 +267,7 @@ func buildOptions(ctx context.Context, path string) (O.Options, error) { slog.Info("Using SOCKS proxy for inbound as per environment variable", "socksAddr", socksAddr) addrPort, err := netip.ParseAddrPort(socksAddr) if err != nil { - return O.Options{}, fmt.Errorf("invalid SOCKS address: %w", err) + return buildOptionsResult{}, fmt.Errorf("invalid SOCKS address: %w", err) } addr := badoption.Addr(addrPort.Addr()) socksIn := O.Inbound{ @@ -292,7 +299,7 @@ func buildOptions(ctx context.Context, path string) (O.Options, error) { cfg, err := loadConfig(confPath) if err != nil { slog.Error("Failed to load config options", "error", err) - return O.Options{}, err + return buildOptionsResult{}, err } // add smart routing and ad block rules @@ -325,7 +332,7 @@ func buildOptions(ctx context.Context, path string) (O.Options, error) { userOpts, err := loadUserOptions(path) if err != nil { slog.Error("Failed to load user servers", "error", err) - return O.Options{}, err + return buildOptionsResult{}, err } var userTags []string if len(userOpts.Outbounds) == 0 && len(userOpts.Endpoints) == 0 { @@ -337,7 +344,7 @@ func buildOptions(ctx context.Context, path string) (O.Options, error) { appendGroupOutbounds(&opts, servers.SGUser, autoUserTag, userTags, nil) if len(lanternTags) == 0 && len(userTags) == 0 { - return O.Options{}, errors.New("no outbounds or endpoints found in config or user servers") + return buildOptionsResult{}, errors.New("no outbounds or endpoints found in config or user servers") } // Add auto all outbound @@ -356,7 +363,10 @@ func buildOptions(ctx context.Context, path string) (O.Options, error) { attribute.String("options", string(writeBoxOptions(path, opts))), attribute.String("env", common.Env()), )) - return opts, nil + return buildOptionsResult{ + Options: opts, + BanditThroughputURL: cfg.BanditThroughputURL, + }, nil } const debugLanternBoxOptionsFilename = "debug-lantern-box-options.json" diff --git a/vpn/boxoptions_test.go b/vpn/boxoptions_test.go index edcc0571..e0e5a3bd 100644 --- a/vpn/boxoptions_test.go +++ b/vpn/boxoptions_test.go @@ -92,14 +92,14 @@ func TestBuildOptions(t *testing.T) { if len(tt.userTags) > 0 { testOptsToFile(t, svrs, filepath.Join(path, common.ServersFileName)) } - opts, err := buildOptions(context.Background(), path) + result, err := buildOptions(context.Background(), path) if tt.shouldError { require.Error(t, err, "expected error but got none") return } require.NoError(t, err) - gotOutbounds := opts.Outbounds + gotOutbounds := result.Options.Outbounds require.NotEmpty(t, gotOutbounds, "no outbounds in built options") assert.NotNil(t, findOutbound(gotOutbounds, constant.TypeDirect), "direct outbound not found") @@ -196,8 +196,9 @@ func TestBuildOptions_Rulesets(t *testing.T) { t.Cleanup(settings.Reset) settings.Set(settings.SmartRoutingKey, true) - options, err := buildOptions(context.Background(), tmp) + result, err := buildOptions(context.Background(), tmp) require.NoError(t, err) + options := result.Options // check rules, rulesets, and outbounds are correctly built into options assert.True(t, contains(t, options.Route.Rules, wantSmartRoutingOpts.Route.Rules[0]), "missing smart routing rule") assert.True(t, contains(t, options.Route.RuleSet, wantSmartRoutingOpts.Route.RuleSet[0]), "missing smart routing ruleset") @@ -211,8 +212,9 @@ func TestBuildOptions_Rulesets(t *testing.T) { t.Cleanup(settings.Reset) settings.Set(settings.AdBlockKey, true) - options, err := buildOptions(context.Background(), tmp) + result, err := buildOptions(context.Background(), tmp) require.NoError(t, err) + options := result.Options // check reject rule and rulesets are correctly built into options for _, rs := range wantAdBlockOpts.Route.RuleSet { assert.True(t, contains(t, options.Route.RuleSet, rs), "missing ad block ruleset") @@ -242,10 +244,10 @@ func TestBuildOptions_BanditURLOverrides(t *testing.T) { path := t.TempDir() testOptsToFile(t, cfg, filepath.Join(path, common.ConfigFileName)) - opts, err := buildOptions(context.Background(), path) + result, err := buildOptions(context.Background(), path) require.NoError(t, err) - out := findOutbound(opts.Outbounds, autoLanternTag) + out := findOutbound(result.Options.Outbounds, autoLanternTag) require.NotNil(t, out, "auto-lantern outbound not found") mutOpts, ok := out.Options.(*lbO.MutableURLTestOutboundOptions) diff --git a/vpn/service.go b/vpn/service.go index cfb5cec6..2fbcc91f 100644 --- a/vpn/service.go +++ b/vpn/service.go @@ -86,17 +86,23 @@ func (s *TunnelService) Start(ctx context.Context, group string, tag string) err func (s *TunnelService) start(ctx context.Context) error { path := settings.GetString(settings.DataPathKey) _ = newSplitTunnel(path) - opts, err := buildOptions(ctx, path) + result, err := buildOptions(ctx, path) if err != nil { return fmt.Errorf("failed to build options: %w", err) } t := tunnel{ dataPath: path, } - if err := t.start(opts, s.platformIfce); err != nil { + if err := t.start(result.Options, s.platformIfce); err != nil { return fmt.Errorf("failed to start tunnel: %w", err) } s.tunnel = &t + + if result.BanditThroughputURL != "" { + reporter := NewThroughputReporter(result.BanditThroughputURL) + go reporter.Run(t.ctx) + } + return nil } diff --git a/vpn/throughput_reporter.go b/vpn/throughput_reporter.go new file mode 100644 index 00000000..6b49251a --- /dev/null +++ b/vpn/throughput_reporter.go @@ -0,0 +1,174 @@ +package vpn + +import ( + "bytes" + "context" + "crypto/rand" + "encoding/json" + "log/slog" + "math/big" + "net/http" + "time" + + "github.com/getlantern/radiance/vpn/ipc" +) + +const ( + throughputReportInterval = 3 * time.Minute + throughputReportJitter = 60 // seconds of random jitter added to each interval + throughputReportTimeout = 10 * time.Second + throughputPaddingMax = 512 // max random padding bytes appended to request body +) + +type throughputReport struct { + Tag string `json:"tag"` + BytesDown int64 `json:"bytes_down"` + BytesUp int64 `json:"bytes_up"` + DurationMs int64 `json:"duration_ms"` +} + +type throughputRequest struct { + Reports []throughputReport `json:"reports"` + Padding string `json:"padding,omitempty"` +} + +// ThroughputReporter periodically aggregates per-outbound throughput from +// real traffic and POSTs it to the bandit throughput URL. +type ThroughputReporter struct { + throughputURL string + interval time.Duration + prevBytes map[string]int64 // outbound tag -> cumulative downlink bytes at last report + prevTime time.Time +} + +// NewThroughputReporter creates a reporter that will POST throughput data +// to the given URL at regular intervals. +func NewThroughputReporter(throughputURL string) *ThroughputReporter { + return &ThroughputReporter{ + throughputURL: throughputURL, + interval: throughputReportInterval, + prevBytes: make(map[string]int64), + } +} + +// Run starts the throughput reporting loop. It blocks until ctx is canceled. +func (r *ThroughputReporter) Run(ctx context.Context) { + for { + jitter := randIntn(throughputReportJitter) + delay := r.interval + time.Duration(jitter)*time.Second + select { + case <-ctx.Done(): + return + case <-time.After(delay): + r.report(ctx) + } + } +} + +func (r *ThroughputReporter) report(ctx context.Context) { + conns, err := ipc.GetConnections(ctx) + if err != nil { + slog.Debug("throughput reporter: failed to get connections", "error", err) + return + } + + now := time.Now() + + // Aggregate bytes per outbound tag (FromOutbound field). + currentBytes := make(map[string]int64) + for _, conn := range conns { + if conn.FromOutbound == "" { + continue + } + currentBytes[conn.FromOutbound] += conn.Downlink + } + + // On the first report, just record the baseline. + if r.prevTime.IsZero() { + r.prevBytes = currentBytes + r.prevTime = now + return + } + + durationMs := now.Sub(r.prevTime).Milliseconds() + if durationMs <= 0 { + return + } + + var reports []throughputReport + for tag, currDown := range currentBytes { + prevDown := r.prevBytes[tag] + delta := currDown - prevDown + if delta <= 0 { + continue + } + reports = append(reports, throughputReport{ + Tag: tag, + BytesDown: delta, + DurationMs: durationMs, + }) + } + + r.prevBytes = currentBytes + r.prevTime = now + + if len(reports) == 0 { + return + } + + body, err := json.Marshal(throughputRequest{ + Reports: reports, + Padding: randPadding(randIntn(throughputPaddingMax)), + }) + if err != nil { + slog.Debug("throughput reporter: failed to marshal request", "error", err) + return + } + + reqCtx, cancel := context.WithTimeout(ctx, throughputReportTimeout) + defer cancel() + + req, err := http.NewRequestWithContext(reqCtx, http.MethodPost, r.throughputURL, bytes.NewReader(body)) + if err != nil { + slog.Debug("throughput reporter: failed to create request", "error", err) + return + } + req.Header.Set("Content-Type", "application/json") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + slog.Debug("throughput reporter: failed to send report", "error", err) + return + } + resp.Body.Close() + + slog.Debug("throughput reporter: sent report", + "reports", len(reports), + "status", resp.StatusCode, + ) +} + +// randIntn returns a cryptographically random int in [0, n). +func randIntn(n int) int { + if n <= 0 { + return 0 + } + v, err := rand.Int(rand.Reader, big.NewInt(int64(n))) + if err != nil { + return 0 + } + return int(v.Int64()) +} + +// randPadding returns a string of n random alphanumeric characters. +func randPadding(n int) string { + if n <= 0 { + return "" + } + const alphabet = "abcdefghijklmnopqrstuvwxyz0123456789" + buf := make([]byte, n) + for i := range buf { + buf[i] = alphabet[randIntn(len(alphabet))] + } + return string(buf) +} From adf8ba51014f884b2b031071a61741ff4fcedca0 Mon Sep 17 00:00:00 2001 From: Adam Fisk Date: Tue, 24 Feb 2026 12:18:58 -0700 Subject: [PATCH 05/14] Revert "Add throughput reporter with jittered timing and padded requests" This reverts commit 7b3bd80ac2fbcab1ab4f7a863817e7a2e9691cb5. --- go.mod | 2 +- go.sum | 4 +- vpn/boxoptions.go | 22 ++--- vpn/boxoptions_test.go | 14 ++- vpn/service.go | 10 +-- vpn/throughput_reporter.go | 174 ------------------------------------- 6 files changed, 17 insertions(+), 209 deletions(-) delete mode 100644 vpn/throughput_reporter.go diff --git a/go.mod b/go.mod index caf501f4..ef1952db 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/alitto/pond v1.9.2 github.com/getlantern/amp v0.0.0-20260113204224-600f8e8dfe5f github.com/getlantern/appdir v0.0.0-20250324200952-507a0625eb01 - github.com/getlantern/common v1.2.1-0.20260224165050-c96e51281bb8 + github.com/getlantern/common v1.2.1-0.20260220164942-f4c071c02fcb github.com/getlantern/dnstt v0.0.0-20250530230749-4d64f4edcf0f github.com/getlantern/fronted v0.0.0-20260121001528-92134131dcd2 github.com/getlantern/keepcurrent v0.0.0-20240126172110-2e0264ca385d diff --git a/go.sum b/go.sum index cc30f184..c6ef719b 100644 --- a/go.sum +++ b/go.sum @@ -192,8 +192,8 @@ github.com/getlantern/amp v0.0.0-20260113204224-600f8e8dfe5f h1:NLGftemDrbGf7Wce github.com/getlantern/amp v0.0.0-20260113204224-600f8e8dfe5f/go.mod h1:qnMv9szb8JK3kA9W4N2FlYUMj1GkA0x7QEUEPD7tk4o= github.com/getlantern/appdir v0.0.0-20250324200952-507a0625eb01 h1:Mmeh4/DA1OKN9tVWRAvTL5efFx4c7v9/55hoK17NclA= github.com/getlantern/appdir v0.0.0-20250324200952-507a0625eb01/go.mod h1:3vR6+jQdWfWojZ77w+htCqEF5MO/Y2twJOpAvFuM9po= -github.com/getlantern/common v1.2.1-0.20260224165050-c96e51281bb8 h1:zFDasLgbFLsXK13+62i3gKePi74q6olfh5mKN2+4BXY= -github.com/getlantern/common v1.2.1-0.20260224165050-c96e51281bb8/go.mod h1:eSSuV4bMPgQJnczBw+KWWqWNo1itzmVxC++qUBPRTt0= +github.com/getlantern/common v1.2.1-0.20260220164942-f4c071c02fcb h1:zTk63u9gyCXfraRjheUJm+wXh+RNaMLfReE4voNRp38= +github.com/getlantern/common v1.2.1-0.20260220164942-f4c071c02fcb/go.mod h1:eSSuV4bMPgQJnczBw+KWWqWNo1itzmVxC++qUBPRTt0= github.com/getlantern/context v0.0.0-20190109183933-c447772a6520/go.mod h1:L+mq6/vvYHKjCX2oez0CgEAJmbq1fbb/oNJIWQkBybY= github.com/getlantern/context v0.0.0-20220418194847-3d5e7a086201 h1:oEZYEpZo28Wdx+5FZo4aU7JFXu0WG/4wJWese5reQSA= github.com/getlantern/context v0.0.0-20220418194847-3d5e7a086201/go.mod h1:Y9WZUHEb+mpra02CbQ/QczLUe6f0Dezxaw5DCJlJQGo= diff --git a/vpn/boxoptions.go b/vpn/boxoptions.go index 0a5ce110..39856cb2 100644 --- a/vpn/boxoptions.go +++ b/vpn/boxoptions.go @@ -237,15 +237,8 @@ func baseRoutingRules() []O.Rule { return rules } -// buildOptionsResult bundles the outputs of buildOptions so callers can -// access both the sing-box options and bandit metadata. -type buildOptionsResult struct { - Options O.Options - BanditThroughputURL string -} - // buildOptions builds the box options using the config options and user servers. -func buildOptions(ctx context.Context, path string) (buildOptionsResult, error) { +func buildOptions(ctx context.Context, path string) (O.Options, error) { ctx, span := otel.Tracer(tracerName).Start(ctx, "buildOptions") defer span.End() @@ -267,7 +260,7 @@ func buildOptions(ctx context.Context, path string) (buildOptionsResult, error) slog.Info("Using SOCKS proxy for inbound as per environment variable", "socksAddr", socksAddr) addrPort, err := netip.ParseAddrPort(socksAddr) if err != nil { - return buildOptionsResult{}, fmt.Errorf("invalid SOCKS address: %w", err) + return O.Options{}, fmt.Errorf("invalid SOCKS address: %w", err) } addr := badoption.Addr(addrPort.Addr()) socksIn := O.Inbound{ @@ -299,7 +292,7 @@ func buildOptions(ctx context.Context, path string) (buildOptionsResult, error) cfg, err := loadConfig(confPath) if err != nil { slog.Error("Failed to load config options", "error", err) - return buildOptionsResult{}, err + return O.Options{}, err } // add smart routing and ad block rules @@ -332,7 +325,7 @@ func buildOptions(ctx context.Context, path string) (buildOptionsResult, error) userOpts, err := loadUserOptions(path) if err != nil { slog.Error("Failed to load user servers", "error", err) - return buildOptionsResult{}, err + return O.Options{}, err } var userTags []string if len(userOpts.Outbounds) == 0 && len(userOpts.Endpoints) == 0 { @@ -344,7 +337,7 @@ func buildOptions(ctx context.Context, path string) (buildOptionsResult, error) appendGroupOutbounds(&opts, servers.SGUser, autoUserTag, userTags, nil) if len(lanternTags) == 0 && len(userTags) == 0 { - return buildOptionsResult{}, errors.New("no outbounds or endpoints found in config or user servers") + return O.Options{}, errors.New("no outbounds or endpoints found in config or user servers") } // Add auto all outbound @@ -363,10 +356,7 @@ func buildOptions(ctx context.Context, path string) (buildOptionsResult, error) attribute.String("options", string(writeBoxOptions(path, opts))), attribute.String("env", common.Env()), )) - return buildOptionsResult{ - Options: opts, - BanditThroughputURL: cfg.BanditThroughputURL, - }, nil + return opts, nil } const debugLanternBoxOptionsFilename = "debug-lantern-box-options.json" diff --git a/vpn/boxoptions_test.go b/vpn/boxoptions_test.go index e0e5a3bd..edcc0571 100644 --- a/vpn/boxoptions_test.go +++ b/vpn/boxoptions_test.go @@ -92,14 +92,14 @@ func TestBuildOptions(t *testing.T) { if len(tt.userTags) > 0 { testOptsToFile(t, svrs, filepath.Join(path, common.ServersFileName)) } - result, err := buildOptions(context.Background(), path) + opts, err := buildOptions(context.Background(), path) if tt.shouldError { require.Error(t, err, "expected error but got none") return } require.NoError(t, err) - gotOutbounds := result.Options.Outbounds + gotOutbounds := opts.Outbounds require.NotEmpty(t, gotOutbounds, "no outbounds in built options") assert.NotNil(t, findOutbound(gotOutbounds, constant.TypeDirect), "direct outbound not found") @@ -196,9 +196,8 @@ func TestBuildOptions_Rulesets(t *testing.T) { t.Cleanup(settings.Reset) settings.Set(settings.SmartRoutingKey, true) - result, err := buildOptions(context.Background(), tmp) + options, err := buildOptions(context.Background(), tmp) require.NoError(t, err) - options := result.Options // check rules, rulesets, and outbounds are correctly built into options assert.True(t, contains(t, options.Route.Rules, wantSmartRoutingOpts.Route.Rules[0]), "missing smart routing rule") assert.True(t, contains(t, options.Route.RuleSet, wantSmartRoutingOpts.Route.RuleSet[0]), "missing smart routing ruleset") @@ -212,9 +211,8 @@ func TestBuildOptions_Rulesets(t *testing.T) { t.Cleanup(settings.Reset) settings.Set(settings.AdBlockKey, true) - result, err := buildOptions(context.Background(), tmp) + options, err := buildOptions(context.Background(), tmp) require.NoError(t, err) - options := result.Options // check reject rule and rulesets are correctly built into options for _, rs := range wantAdBlockOpts.Route.RuleSet { assert.True(t, contains(t, options.Route.RuleSet, rs), "missing ad block ruleset") @@ -244,10 +242,10 @@ func TestBuildOptions_BanditURLOverrides(t *testing.T) { path := t.TempDir() testOptsToFile(t, cfg, filepath.Join(path, common.ConfigFileName)) - result, err := buildOptions(context.Background(), path) + opts, err := buildOptions(context.Background(), path) require.NoError(t, err) - out := findOutbound(result.Options.Outbounds, autoLanternTag) + out := findOutbound(opts.Outbounds, autoLanternTag) require.NotNil(t, out, "auto-lantern outbound not found") mutOpts, ok := out.Options.(*lbO.MutableURLTestOutboundOptions) diff --git a/vpn/service.go b/vpn/service.go index 2fbcc91f..cfb5cec6 100644 --- a/vpn/service.go +++ b/vpn/service.go @@ -86,23 +86,17 @@ func (s *TunnelService) Start(ctx context.Context, group string, tag string) err func (s *TunnelService) start(ctx context.Context) error { path := settings.GetString(settings.DataPathKey) _ = newSplitTunnel(path) - result, err := buildOptions(ctx, path) + opts, err := buildOptions(ctx, path) if err != nil { return fmt.Errorf("failed to build options: %w", err) } t := tunnel{ dataPath: path, } - if err := t.start(result.Options, s.platformIfce); err != nil { + if err := t.start(opts, s.platformIfce); err != nil { return fmt.Errorf("failed to start tunnel: %w", err) } s.tunnel = &t - - if result.BanditThroughputURL != "" { - reporter := NewThroughputReporter(result.BanditThroughputURL) - go reporter.Run(t.ctx) - } - return nil } diff --git a/vpn/throughput_reporter.go b/vpn/throughput_reporter.go deleted file mode 100644 index 6b49251a..00000000 --- a/vpn/throughput_reporter.go +++ /dev/null @@ -1,174 +0,0 @@ -package vpn - -import ( - "bytes" - "context" - "crypto/rand" - "encoding/json" - "log/slog" - "math/big" - "net/http" - "time" - - "github.com/getlantern/radiance/vpn/ipc" -) - -const ( - throughputReportInterval = 3 * time.Minute - throughputReportJitter = 60 // seconds of random jitter added to each interval - throughputReportTimeout = 10 * time.Second - throughputPaddingMax = 512 // max random padding bytes appended to request body -) - -type throughputReport struct { - Tag string `json:"tag"` - BytesDown int64 `json:"bytes_down"` - BytesUp int64 `json:"bytes_up"` - DurationMs int64 `json:"duration_ms"` -} - -type throughputRequest struct { - Reports []throughputReport `json:"reports"` - Padding string `json:"padding,omitempty"` -} - -// ThroughputReporter periodically aggregates per-outbound throughput from -// real traffic and POSTs it to the bandit throughput URL. -type ThroughputReporter struct { - throughputURL string - interval time.Duration - prevBytes map[string]int64 // outbound tag -> cumulative downlink bytes at last report - prevTime time.Time -} - -// NewThroughputReporter creates a reporter that will POST throughput data -// to the given URL at regular intervals. -func NewThroughputReporter(throughputURL string) *ThroughputReporter { - return &ThroughputReporter{ - throughputURL: throughputURL, - interval: throughputReportInterval, - prevBytes: make(map[string]int64), - } -} - -// Run starts the throughput reporting loop. It blocks until ctx is canceled. -func (r *ThroughputReporter) Run(ctx context.Context) { - for { - jitter := randIntn(throughputReportJitter) - delay := r.interval + time.Duration(jitter)*time.Second - select { - case <-ctx.Done(): - return - case <-time.After(delay): - r.report(ctx) - } - } -} - -func (r *ThroughputReporter) report(ctx context.Context) { - conns, err := ipc.GetConnections(ctx) - if err != nil { - slog.Debug("throughput reporter: failed to get connections", "error", err) - return - } - - now := time.Now() - - // Aggregate bytes per outbound tag (FromOutbound field). - currentBytes := make(map[string]int64) - for _, conn := range conns { - if conn.FromOutbound == "" { - continue - } - currentBytes[conn.FromOutbound] += conn.Downlink - } - - // On the first report, just record the baseline. - if r.prevTime.IsZero() { - r.prevBytes = currentBytes - r.prevTime = now - return - } - - durationMs := now.Sub(r.prevTime).Milliseconds() - if durationMs <= 0 { - return - } - - var reports []throughputReport - for tag, currDown := range currentBytes { - prevDown := r.prevBytes[tag] - delta := currDown - prevDown - if delta <= 0 { - continue - } - reports = append(reports, throughputReport{ - Tag: tag, - BytesDown: delta, - DurationMs: durationMs, - }) - } - - r.prevBytes = currentBytes - r.prevTime = now - - if len(reports) == 0 { - return - } - - body, err := json.Marshal(throughputRequest{ - Reports: reports, - Padding: randPadding(randIntn(throughputPaddingMax)), - }) - if err != nil { - slog.Debug("throughput reporter: failed to marshal request", "error", err) - return - } - - reqCtx, cancel := context.WithTimeout(ctx, throughputReportTimeout) - defer cancel() - - req, err := http.NewRequestWithContext(reqCtx, http.MethodPost, r.throughputURL, bytes.NewReader(body)) - if err != nil { - slog.Debug("throughput reporter: failed to create request", "error", err) - return - } - req.Header.Set("Content-Type", "application/json") - - resp, err := http.DefaultClient.Do(req) - if err != nil { - slog.Debug("throughput reporter: failed to send report", "error", err) - return - } - resp.Body.Close() - - slog.Debug("throughput reporter: sent report", - "reports", len(reports), - "status", resp.StatusCode, - ) -} - -// randIntn returns a cryptographically random int in [0, n). -func randIntn(n int) int { - if n <= 0 { - return 0 - } - v, err := rand.Int(rand.Reader, big.NewInt(int64(n))) - if err != nil { - return 0 - } - return int(v.Int64()) -} - -// randPadding returns a string of n random alphanumeric characters. -func randPadding(n int) string { - if n <= 0 { - return "" - } - const alphabet = "abcdefghijklmnopqrstuvwxyz0123456789" - buf := make([]byte, n) - for i := range buf { - buf[i] = alphabet[randIntn(len(alphabet))] - } - return string(buf) -} From 94f125ce00e59e90dd89736666ddda089d83e650 Mon Sep 17 00:00:00 2001 From: Adam Fisk Date: Tue, 24 Feb 2026 12:39:08 -0700 Subject: [PATCH 06/14] Updated to mainline of lantern-box --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ef1952db..4adb6aac 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,7 @@ require ( github.com/getlantern/fronted v0.0.0-20260121001528-92134131dcd2 github.com/getlantern/keepcurrent v0.0.0-20240126172110-2e0264ca385d github.com/getlantern/kindling v0.0.0-20260105215242-8df765e82461 - github.com/getlantern/lantern-box v0.0.6-0.20260220165004-bff0fac7d776 + github.com/getlantern/lantern-box v0.0.6-0.20260224193811-b50ea1f2e9db github.com/getlantern/pluriconfig v0.0.0-20251126214241-8cc8bc561535 github.com/getlantern/timezone v0.0.0-20210901200113-3f9de9d360c9 github.com/go-resty/resty/v2 v2.16.5 diff --git a/go.sum b/go.sum index c6ef719b..1bfe6031 100644 --- a/go.sum +++ b/go.sum @@ -221,8 +221,8 @@ github.com/getlantern/keepcurrent v0.0.0-20240126172110-2e0264ca385d h1:2/9rPC1x github.com/getlantern/keepcurrent v0.0.0-20240126172110-2e0264ca385d/go.mod h1:enUAvxkJ15QUtTKOKoO9WJV2L5u33P8YmqkC+iu8iT4= github.com/getlantern/kindling v0.0.0-20260105215242-8df765e82461 h1:PwgggQLmIFSjlMr5SgvO0M8jK3XjrJsxLu7IccACs2Y= github.com/getlantern/kindling v0.0.0-20260105215242-8df765e82461/go.mod h1:eg1zov65hEXytiISbE4lcExN3gVhi+ZnPxkoiuVzC6c= -github.com/getlantern/lantern-box v0.0.6-0.20260220165004-bff0fac7d776 h1:Z9515MBli04kLtymOIy0Z312N/L5heUQ772d1Ttc+JM= -github.com/getlantern/lantern-box v0.0.6-0.20260220165004-bff0fac7d776/go.mod h1:x4kOCOi7SdaWFfod6LpvfEwkzZCbeJzWQcoNsY7yeFk= +github.com/getlantern/lantern-box v0.0.6-0.20260224193811-b50ea1f2e9db h1:N+qr2VV6w8hpXBQuDHI/u4/3fQzgFRhV1dM47GyNi5M= +github.com/getlantern/lantern-box v0.0.6-0.20260224193811-b50ea1f2e9db/go.mod h1:OnSmUR2+rpmGcS5DA0iUyEPwfPEEftnEtj2A6rBq+ko= github.com/getlantern/lantern-water v0.0.0-20260130212632-d5ea08838250 h1:xculJyC6hS0kNSQKWBP1FQbpSVmeJyhUGID804jgKCA= github.com/getlantern/lantern-water v0.0.0-20260130212632-d5ea08838250/go.mod h1:ZpSOrcdJkmb8MvaQn6mxaidxshlyi+RJLUerhW4L5Lo= github.com/getlantern/mockconn v0.0.0-20200818071412-cb30d065a848 h1:2MhMMVBTnaHrst6HyWFDhwQCaJ05PZuOv1bE2gN8WFY= From 4795b5eccaa2bc5d360c6e794cb1081d7c90c85f Mon Sep 17 00:00:00 2001 From: Adam Fisk Date: Wed, 25 Feb 2026 15:41:38 -0700 Subject: [PATCH 07/14] Wire BanditURLOverrides through config updates to live URL test groups Persist URLOverrides in servers.Options so they survive file-watcher reloads, and call SetURLOverrides in updateGroup so new proxies from a config refresh get their bandit callback URLs. Co-Authored-By: Claude Opus 4.6 --- config/config.go | 7 ++++--- servers/manager.go | 7 ++++--- vpn/tunnel.go | 4 ++++ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/config/config.go b/config/config.go index 20fb84df..e87b9100 100644 --- a/config/config.go +++ b/config/config.go @@ -234,9 +234,10 @@ func (ch *ConfigHandler) fetchConfig() error { locs[k] = *v } opts := servers.Options{ - Outbounds: cfg.Options.Outbounds, - Endpoints: cfg.Options.Endpoints, - Locations: locs, + Outbounds: cfg.Options.Outbounds, + Endpoints: cfg.Options.Endpoints, + Locations: locs, + URLOverrides: cfg.BanditURLOverrides, } if err := ch.svrManager.SetServers(servers.SGLantern, opts); err != nil { slog.Error("setting servers in manager", "error", err) diff --git a/servers/manager.go b/servers/manager.go index 9a101aed..c5d46e6e 100644 --- a/servers/manager.go +++ b/servers/manager.go @@ -50,9 +50,10 @@ const ( ) type Options struct { - Outbounds []option.Outbound `json:"outbounds,omitempty"` - Endpoints []option.Endpoint `json:"endpoints,omitempty"` - Locations map[string]C.ServerLocation `json:"locations,omitempty"` + Outbounds []option.Outbound `json:"outbounds,omitempty"` + Endpoints []option.Endpoint `json:"endpoints,omitempty"` + Locations map[string]C.ServerLocation `json:"locations,omitempty"` + URLOverrides map[string]string `json:"url_overrides,omitempty"` } // AllTags returns a slice of all tags from both endpoints and outbounds in the Options. diff --git a/vpn/tunnel.go b/vpn/tunnel.go index 4c650eec..d64f0f73 100644 --- a/vpn/tunnel.go +++ b/vpn/tunnel.go @@ -481,6 +481,10 @@ func (t *tunnel) updateGroup(group string, newOpts servers.Options) error { } } + if err := t.mutGrpMgr.SetURLOverrides(autoTag, newOpts.URLOverrides); err != nil { + slog.Warn("Failed to set URL overrides", "group", autoTag, "error", err) + } + slog.Debug("Updated servers in group", "group", group, "added", added, "removed", removed) return errors.Join(errs...) } From 3b6ed073e3b34ff28bec118835543537d2f46ae1 Mon Sep 17 00:00:00 2001 From: Adam Fisk Date: Wed, 25 Feb 2026 17:20:48 -0700 Subject: [PATCH 08/14] Address PR review feedback - Fix URLOverrides lost through removeDuplicates: stash overrides before dedup and pass the stashed value to SetURLOverrides - Persist URLOverrides through servers.Manager: clone in setServers and Servers so overrides survive save/load round-trips - go.sum updated by go mod tidy Co-Authored-By: Claude Opus 4.6 --- go.sum | 2 -- servers/manager.go | 14 ++++++++------ vpn/tunnel.go | 4 +++- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/go.sum b/go.sum index 1bfe6031..74e32e8c 100644 --- a/go.sum +++ b/go.sum @@ -221,8 +221,6 @@ github.com/getlantern/keepcurrent v0.0.0-20240126172110-2e0264ca385d h1:2/9rPC1x github.com/getlantern/keepcurrent v0.0.0-20240126172110-2e0264ca385d/go.mod h1:enUAvxkJ15QUtTKOKoO9WJV2L5u33P8YmqkC+iu8iT4= github.com/getlantern/kindling v0.0.0-20260105215242-8df765e82461 h1:PwgggQLmIFSjlMr5SgvO0M8jK3XjrJsxLu7IccACs2Y= github.com/getlantern/kindling v0.0.0-20260105215242-8df765e82461/go.mod h1:eg1zov65hEXytiISbE4lcExN3gVhi+ZnPxkoiuVzC6c= -github.com/getlantern/lantern-box v0.0.6-0.20260224193811-b50ea1f2e9db h1:N+qr2VV6w8hpXBQuDHI/u4/3fQzgFRhV1dM47GyNi5M= -github.com/getlantern/lantern-box v0.0.6-0.20260224193811-b50ea1f2e9db/go.mod h1:OnSmUR2+rpmGcS5DA0iUyEPwfPEEftnEtj2A6rBq+ko= github.com/getlantern/lantern-water v0.0.0-20260130212632-d5ea08838250 h1:xculJyC6hS0kNSQKWBP1FQbpSVmeJyhUGID804jgKCA= github.com/getlantern/lantern-water v0.0.0-20260130212632-d5ea08838250/go.mod h1:ZpSOrcdJkmb8MvaQn6mxaidxshlyi+RJLUerhW4L5Lo= github.com/getlantern/mockconn v0.0.0-20200818071412-cb30d065a848 h1:2MhMMVBTnaHrst6HyWFDhwQCaJ05PZuOv1bE2gN8WFY= diff --git a/servers/manager.go b/servers/manager.go index c5d46e6e..5eec5f55 100644 --- a/servers/manager.go +++ b/servers/manager.go @@ -150,9 +150,10 @@ func (m *Manager) Servers() Servers { result := make(Servers, len(m.servers)) for group, opts := range m.servers { result[group] = Options{ - Outbounds: append([]option.Outbound{}, opts.Outbounds...), - Endpoints: append([]option.Endpoint{}, opts.Endpoints...), - Locations: maps.Clone(opts.Locations), + Outbounds: append([]option.Outbound{}, opts.Outbounds...), + Endpoints: append([]option.Endpoint{}, opts.Endpoints...), + Locations: maps.Clone(opts.Locations), + URLOverrides: maps.Clone(opts.URLOverrides), } } return result @@ -223,9 +224,10 @@ func (m *Manager) setServers(group ServerGroup, options Options) error { slog.Log(nil, internal.LevelTrace, "Setting servers", "group", group, "options", options) opts := Options{ - Outbounds: append([]option.Outbound{}, options.Outbounds...), - Endpoints: append([]option.Endpoint{}, options.Endpoints...), - Locations: make(map[string]C.ServerLocation, len(options.Locations)), + Outbounds: append([]option.Outbound{}, options.Outbounds...), + Endpoints: append([]option.Endpoint{}, options.Endpoints...), + Locations: make(map[string]C.ServerLocation, len(options.Locations)), + URLOverrides: maps.Clone(options.URLOverrides), } if len(options.Locations) > 0 { maps.Copy(opts.Locations, options.Locations) diff --git a/vpn/tunnel.go b/vpn/tunnel.go index d64f0f73..44c4d182 100644 --- a/vpn/tunnel.go +++ b/vpn/tunnel.go @@ -434,6 +434,8 @@ func (t *tunnel) updateGroup(group string, newOpts servers.Options) error { } // remove duplicates from newOpts before adding to avoid unnecessary reloads + // Stash URLOverrides before dedup since removeDuplicates rebuilds the Options struct + urlOverrides := newOpts.URLOverrides newOpts = removeDuplicates(t.optsMap, newOpts, group) // for each outbound/endpoint in new add to group @@ -481,7 +483,7 @@ func (t *tunnel) updateGroup(group string, newOpts servers.Options) error { } } - if err := t.mutGrpMgr.SetURLOverrides(autoTag, newOpts.URLOverrides); err != nil { + if err := t.mutGrpMgr.SetURLOverrides(autoTag, urlOverrides); err != nil { slog.Warn("Failed to set URL overrides", "group", autoTag, "error", err) } From 12c442563b1f0537c69c658b5d5f54d7019469fd Mon Sep 17 00:00:00 2001 From: Adam Fisk Date: Mon, 2 Mar 2026 14:36:41 -0700 Subject: [PATCH 09/14] updated to latest lantern-box and added feature override for testing --- config/fetcher.go | 4 ++++ go.mod | 2 +- go.sum | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/config/fetcher.go b/config/fetcher.go index 8ca614f1..5e5bcae7 100644 --- a/config/fetcher.go +++ b/config/fetcher.go @@ -154,6 +154,10 @@ func (f *fetcher) send(ctx context.Context, body io.Reader) ([]byte, error) { slog.Info("Setting x-lantern-client-country header", "country", val) req.Header.Set("x-lantern-client-country", val) } + if val, exists := os.LookupEnv("RADIANCE_FEATURE_OVERRIDE"); exists { + slog.Info("Setting X-Lantern-Feature-Override header", "features", val) + req.Header.Set("X-Lantern-Feature-Override", val) + } // Add If-Modified-Since header to the request // Note that on the first run, lastModified is zero, so the server will return the latest config. diff --git a/go.mod b/go.mod index 4adb6aac..ab2b5795 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,7 @@ require ( github.com/getlantern/fronted v0.0.0-20260121001528-92134131dcd2 github.com/getlantern/keepcurrent v0.0.0-20240126172110-2e0264ca385d github.com/getlantern/kindling v0.0.0-20260105215242-8df765e82461 - github.com/getlantern/lantern-box v0.0.6-0.20260224193811-b50ea1f2e9db + github.com/getlantern/lantern-box v0.0.6-0.20260302213426-7e099a46ec8e github.com/getlantern/pluriconfig v0.0.0-20251126214241-8cc8bc561535 github.com/getlantern/timezone v0.0.0-20210901200113-3f9de9d360c9 github.com/go-resty/resty/v2 v2.16.5 diff --git a/go.sum b/go.sum index 74e32e8c..5788445c 100644 --- a/go.sum +++ b/go.sum @@ -221,6 +221,8 @@ github.com/getlantern/keepcurrent v0.0.0-20240126172110-2e0264ca385d h1:2/9rPC1x github.com/getlantern/keepcurrent v0.0.0-20240126172110-2e0264ca385d/go.mod h1:enUAvxkJ15QUtTKOKoO9WJV2L5u33P8YmqkC+iu8iT4= github.com/getlantern/kindling v0.0.0-20260105215242-8df765e82461 h1:PwgggQLmIFSjlMr5SgvO0M8jK3XjrJsxLu7IccACs2Y= github.com/getlantern/kindling v0.0.0-20260105215242-8df765e82461/go.mod h1:eg1zov65hEXytiISbE4lcExN3gVhi+ZnPxkoiuVzC6c= +github.com/getlantern/lantern-box v0.0.6-0.20260302213426-7e099a46ec8e h1:hk5BPqpiVqtpaJYOCydcKiAVbIjFN/YtTHqvSh/JVOQ= +github.com/getlantern/lantern-box v0.0.6-0.20260302213426-7e099a46ec8e/go.mod h1:OnSmUR2+rpmGcS5DA0iUyEPwfPEEftnEtj2A6rBq+ko= github.com/getlantern/lantern-water v0.0.0-20260130212632-d5ea08838250 h1:xculJyC6hS0kNSQKWBP1FQbpSVmeJyhUGID804jgKCA= github.com/getlantern/lantern-water v0.0.0-20260130212632-d5ea08838250/go.mod h1:ZpSOrcdJkmb8MvaQn6mxaidxshlyi+RJLUerhW4L5Lo= github.com/getlantern/mockconn v0.0.0-20200818071412-cb30d065a848 h1:2MhMMVBTnaHrst6HyWFDhwQCaJ05PZuOv1bE2gN8WFY= From 890c8320f1910e968859fc2be7b3051619466e16 Mon Sep 17 00:00:00 2001 From: Adam Fisk Date: Mon, 2 Mar 2026 14:38:21 -0700 Subject: [PATCH 10/14] Updated README with RADIANCE_FEATURE_OVERRIDE details --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0499e10f..4a691eec 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ Available variables: * `RADIANCE_USE_SOCKS_PROXY`: If set to `true`, replace the TUN with a SOCKS proxy for inbound connections. * `RADIANCE_SOCKS_ADDRESS`: Specifies the address (`host:port`) for the SOCKS proxy to use for inbound connections. * `RADIANCE_ENV`: Sets whether we're running in production or development mode. Set to `dev` for additional debugging output, such as the sing-box config actually in use. `prod` is the default. +* `RADIANCE_FEATURE_OVERRIDE`: Comma-separated list of feature flags to force-enable on the server side (non-prod environments only). The value is sent as the `X-Lantern-Feature-Override` header on config requests. For example, `RADIANCE_FEATURE_OVERRIDE=bandit_assignment` enables bandit-based proxy assignment during testing. ## Packages From 2a6ff51875e4be856216da2386627d6ef6771835 Mon Sep 17 00:00:00 2001 From: Adam Fisk Date: Mon, 2 Mar 2026 14:58:46 -0700 Subject: [PATCH 11/14] Merge URLOverrides in Manager.merge() for AddServers path The merge() function rebuilds Options without carrying URLOverrides, so AddServers would silently drop them. Merge incoming URLOverrides into the existing group's map alongside Locations. Co-Authored-By: Claude Opus 4.6 --- servers/manager.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/servers/manager.go b/servers/manager.go index 5eec5f55..fe7101da 100644 --- a/servers/manager.go +++ b/servers/manager.go @@ -300,6 +300,12 @@ func (m *Manager) merge(group ServerGroup, options Options) []string { servers.Outbounds = append(servers.Outbounds, out) servers.Locations[out.Tag] = options.Locations[out.Tag] } + for k, v := range options.URLOverrides { + if servers.URLOverrides == nil { + servers.URLOverrides = make(map[string]string) + } + servers.URLOverrides[k] = v + } m.servers[group] = servers return existingTags } From b1fbf185c814514d108ffc04a1de926c6c04eefc Mon Sep 17 00:00:00 2001 From: Adam Fisk Date: Mon, 23 Mar 2026 10:00:45 -0600 Subject: [PATCH 12/14] Fix URL test interval for bandit and add override logging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - boxoptions.go: Reduce URL test interval from 3min to 15s so bandit callbacks arrive within the 20s probe expiry window. Reduce idle timeout from 15min to 5min. - tunnel.go: Log when bandit URL overrides are applied to a URL test group, including the override count. - config.go: Log when config response includes bandit URL overrides with counts for overrides, outbounds, and endpoints. The 3-minute interval was the root cause of high failure rates — probes expire in 20s but the client wouldn't test until 3 minutes later, so the reaper always recorded reward=0. Co-Authored-By: Claude Opus 4.6 (1M context) --- config/config.go | 7 +++++++ vpn/boxoptions.go | 4 ++-- vpn/tunnel.go | 6 ++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/config/config.go b/config/config.go index e87b9100..183612af 100644 --- a/config/config.go +++ b/config/config.go @@ -239,6 +239,13 @@ func (ch *ConfigHandler) fetchConfig() error { Locations: locs, URLOverrides: cfg.BanditURLOverrides, } + if len(cfg.BanditURLOverrides) > 0 { + slog.Info("Config includes bandit URL overrides", + "override_count", len(cfg.BanditURLOverrides), + "outbound_count", len(cfg.Options.Outbounds), + "endpoint_count", len(cfg.Options.Endpoints), + ) + } if err := ch.svrManager.SetServers(servers.SGLantern, opts); err != nil { slog.Error("setting servers in manager", "error", err) } diff --git a/vpn/boxoptions.go b/vpn/boxoptions.go index ff0e1ed1..57d354c4 100644 --- a/vpn/boxoptions.go +++ b/vpn/boxoptions.go @@ -40,8 +40,8 @@ const ( autoLanternTag = "auto-lantern" autoUserTag = "auto-user" - urlTestInterval = 3 * time.Minute // must be less than urlTestIdleTimeout - urlTestIdleTimeout = 15 * time.Minute + urlTestInterval = 15 * time.Second // must be less than urlTestIdleTimeout; kept short so bandit callbacks arrive within the 20s probe expiry window + urlTestIdleTimeout = 5 * time.Minute cacheID = "lantern" cacheFileName = "lantern.cache" diff --git a/vpn/tunnel.go b/vpn/tunnel.go index 2a2c8735..e1000c9c 100644 --- a/vpn/tunnel.go +++ b/vpn/tunnel.go @@ -341,6 +341,12 @@ func (t *tunnel) addOutbounds(group string, options servers.Options) (err error) } } + if len(options.URLOverrides) > 0 { + slog.Info("Applying bandit URL overrides to URL test group", + "group", autoTag, + "override_count", len(options.URLOverrides), + ) + } if err := t.mutGrpMgr.SetURLOverrides(autoTag, options.URLOverrides); err != nil { slog.Warn("Failed to set URL overrides", "group", autoTag, "error", err) } From 04f3a80f1f32e5720b58eeb2d79369e126f0f94a Mon Sep 17 00:00:00 2001 From: Adam Fisk Date: Mon, 23 Mar 2026 10:05:29 -0600 Subject: [PATCH 13/14] Trigger immediate URL test on fresh bandit config instead of reducing interval Instead of lowering the URL test interval globally (which increases traffic for all users), trigger CheckOutbounds() immediately when a new config with bandit URL overrides arrives from the API. This fires the callbacks within seconds of probe creation. - tunnel.go: After SetURLOverrides, call CheckOutbounds to fire an immediate URL test cycle. Log override application and trigger. - config.go: Log when config response includes bandit URL overrides. - go.mod: Update lantern-box to include OutboundChecker interface. The URL test interval stays at 3 minutes for normal operation. Callbacks only need to be fast on initial config receipt. Co-Authored-By: Claude Opus 4.6 (1M context) --- go.mod | 2 +- go.sum | 4 ++-- vpn/boxoptions.go | 4 ++-- vpn/tunnel.go | 11 +++++++++++ 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 3baed4a6..6f4444e7 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( github.com/getlantern/fronted v0.0.0-20260319225233-cf2160f85053 github.com/getlantern/keepcurrent v0.0.0-20260304213122-017d542145ae github.com/getlantern/kindling v0.0.0-20260319225424-4736208dd171 - github.com/getlantern/lantern-box v0.0.49 + github.com/getlantern/lantern-box v0.0.50-0.20260323160457-f763bd127d8c github.com/getlantern/pluriconfig v0.0.0-20251126214241-8cc8bc561535 github.com/getlantern/timezone v0.0.0-20210901200113-3f9de9d360c9 github.com/go-resty/resty/v2 v2.16.5 diff --git a/go.sum b/go.sum index d137cb9c..ec647fbc 100644 --- a/go.sum +++ b/go.sum @@ -244,8 +244,8 @@ github.com/getlantern/keepcurrent v0.0.0-20260304213122-017d542145ae h1:NMq3K7h3 github.com/getlantern/keepcurrent v0.0.0-20260304213122-017d542145ae/go.mod h1:ag5g9aWUw2FJcX5RVRpJ9EBQBy5yJuy2WXDouIn/m4w= github.com/getlantern/kindling v0.0.0-20260319225424-4736208dd171 h1:UEjX+Gg+T6oGVUbzHJ4JfLhlsIh8Wl8PmTXZYWGS43A= github.com/getlantern/kindling v0.0.0-20260319225424-4736208dd171/go.mod h1:c5cFjpNrqX8wQ0PUE2blHrO7knAlRCVx3j1/G6zaVlY= -github.com/getlantern/lantern-box v0.0.49 h1:ZEurOeyceCkrNbWptrbEhjS5xVphZm1v8XFnISRo8C4= -github.com/getlantern/lantern-box v0.0.49/go.mod h1:Luj0rLyuokADHg2B+eXlAdxVXYO+T5Reeds+hKuQkZA= +github.com/getlantern/lantern-box v0.0.50-0.20260323160457-f763bd127d8c h1:4OhNZ95H/5EqGmuiMD/iDXnHgEsvbe4TafL94hl6YUo= +github.com/getlantern/lantern-box v0.0.50-0.20260323160457-f763bd127d8c/go.mod h1:Luj0rLyuokADHg2B+eXlAdxVXYO+T5Reeds+hKuQkZA= github.com/getlantern/lantern-water v0.0.0-20260317143726-e0ee64a11d90 h1:P9JX1yAu2uq3b5YiT0sLtHkTrkZuttV8gPZh81nUuag= github.com/getlantern/lantern-water v0.0.0-20260317143726-e0ee64a11d90/go.mod h1:3JpJgwi4KEI6rS9loOAvcBp+F2jP65d0tTg2GQcTPBU= github.com/getlantern/ops v0.0.0-20231025133620-f368ab734534 h1:3BwvWj0JZzFEvNNiMhCu4bf60nqcIuQpTYb00Ezm1ag= diff --git a/vpn/boxoptions.go b/vpn/boxoptions.go index 57d354c4..ff0e1ed1 100644 --- a/vpn/boxoptions.go +++ b/vpn/boxoptions.go @@ -40,8 +40,8 @@ const ( autoLanternTag = "auto-lantern" autoUserTag = "auto-user" - urlTestInterval = 15 * time.Second // must be less than urlTestIdleTimeout; kept short so bandit callbacks arrive within the 20s probe expiry window - urlTestIdleTimeout = 5 * time.Minute + urlTestInterval = 3 * time.Minute // must be less than urlTestIdleTimeout + urlTestIdleTimeout = 15 * time.Minute cacheID = "lantern" cacheFileName = "lantern.cache" diff --git a/vpn/tunnel.go b/vpn/tunnel.go index e1000c9c..d0996a6a 100644 --- a/vpn/tunnel.go +++ b/vpn/tunnel.go @@ -351,6 +351,17 @@ func (t *tunnel) addOutbounds(group string, options servers.Options) (err error) slog.Warn("Failed to set URL overrides", "group", autoTag, "error", err) } + // Trigger an immediate URL test cycle when we have bandit overrides so + // callback probes are hit within seconds of config receipt rather than + // waiting for the next scheduled interval (3 min). + if len(options.URLOverrides) > 0 { + if err := t.mutGrpMgr.CheckOutbounds(autoTag); err != nil { + slog.Warn("Failed to trigger immediate URL test after bandit overrides", "group", autoTag, "error", err) + } else { + slog.Info("Triggered immediate URL test for bandit callbacks", "group", autoTag) + } + } + slog.Debug("Added servers to group", "group", group, "added", added) return errors.Join(errs...) } From 44c306269d9035dedc8a664e5ddfaeb1087cb281 Mon Sep 17 00:00:00 2001 From: Adam Fisk Date: Mon, 23 Mar 2026 10:33:45 -0600 Subject: [PATCH 14/14] Address PR review comments - tunnel.go: Carry URLOverrides and Credentials through removeDuplicates so bandit overrides aren't silently dropped (fixes review comment) - tunnel.go: Only trigger CheckOutbounds after SetURLOverrides succeeds to avoid testing against default URLs on failure - fetcher.go: Guard RADIANCE_FEATURE_OVERRIDE with non-empty check to avoid sending an empty X-Lantern-Feature-Override header - README.md: Clarify that feature override works in any environment, not just non-prod Co-Authored-By: Claude Opus 4.6 (1M context) --- README.md | 2 +- config/fetcher.go | 2 +- vpn/tunnel.go | 18 +++++++++--------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 4a691eec..ce688091 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Available variables: * `RADIANCE_USE_SOCKS_PROXY`: If set to `true`, replace the TUN with a SOCKS proxy for inbound connections. * `RADIANCE_SOCKS_ADDRESS`: Specifies the address (`host:port`) for the SOCKS proxy to use for inbound connections. * `RADIANCE_ENV`: Sets whether we're running in production or development mode. Set to `dev` for additional debugging output, such as the sing-box config actually in use. `prod` is the default. -* `RADIANCE_FEATURE_OVERRIDE`: Comma-separated list of feature flags to force-enable on the server side (non-prod environments only). The value is sent as the `X-Lantern-Feature-Override` header on config requests. For example, `RADIANCE_FEATURE_OVERRIDE=bandit_assignment` enables bandit-based proxy assignment during testing. +* `RADIANCE_FEATURE_OVERRIDE`: Comma-separated list of feature flags to force-enable on the server side. If set, the value is sent as the `X-Lantern-Feature-Override` header on config requests in any environment, and it is recommended for testing/non-production use. For example, `RADIANCE_FEATURE_OVERRIDE=bandit_assignment` enables bandit-based proxy assignment during testing. ## Packages diff --git a/config/fetcher.go b/config/fetcher.go index 1b84e5fb..b9419b75 100644 --- a/config/fetcher.go +++ b/config/fetcher.go @@ -153,7 +153,7 @@ func (f *fetcher) send(ctx context.Context, body io.Reader) ([]byte, error) { slog.Info("Setting x-lantern-client-country header", "country", val) req.Header.Set("x-lantern-client-country", val) } - if val, exists := os.LookupEnv("RADIANCE_FEATURE_OVERRIDE"); exists { + if val, exists := os.LookupEnv("RADIANCE_FEATURE_OVERRIDE"); exists && val != "" { slog.Info("Setting X-Lantern-Feature-Override header", "features", val) req.Header.Set("X-Lantern-Feature-Override", val) } diff --git a/vpn/tunnel.go b/vpn/tunnel.go index d0996a6a..6bb6cfc2 100644 --- a/vpn/tunnel.go +++ b/vpn/tunnel.go @@ -349,12 +349,10 @@ func (t *tunnel) addOutbounds(group string, options servers.Options) (err error) } if err := t.mutGrpMgr.SetURLOverrides(autoTag, options.URLOverrides); err != nil { slog.Warn("Failed to set URL overrides", "group", autoTag, "error", err) - } - - // Trigger an immediate URL test cycle when we have bandit overrides so - // callback probes are hit within seconds of config receipt rather than - // waiting for the next scheduled interval (3 min). - if len(options.URLOverrides) > 0 { + } else if len(options.URLOverrides) > 0 { + // Trigger an immediate URL test cycle when we have bandit overrides so + // callback probes are hit within seconds of config receipt rather than + // waiting for the next scheduled interval (3 min). if err := t.mutGrpMgr.CheckOutbounds(autoTag); err != nil { slog.Warn("Failed to trigger immediate URL test after bandit overrides", "group", autoTag, "error", err) } else { @@ -473,9 +471,11 @@ func (t *tunnel) updateOutbounds(new servers.Servers) error { func removeDuplicates(ctx context.Context, curr *lsync.TypedMap[string, []byte], new servers.Options, group string) servers.Options { slog.Log(nil, internal.LevelTrace, "Removing duplicate outbounds/endpoints", "group", group) deduped := servers.Options{ - Outbounds: []O.Outbound{}, - Endpoints: []O.Endpoint{}, - Locations: map[string]lcommon.ServerLocation{}, + Outbounds: []O.Outbound{}, + Endpoints: []O.Endpoint{}, + Locations: map[string]lcommon.ServerLocation{}, + URLOverrides: new.URLOverrides, + Credentials: new.Credentials, } var dropped []string for _, out := range new.Outbounds {