Repository: grpc/grpc-go

Issue:
[protoc-gen-go-grpc] Support Protobuf edition 2024
### What version of gRPC are you using?
> protoc-gen-go-grpc 1.5.1

### What version of Go are you using (`go version`)?
> go version go1.25.2 darwin/arm64

### What operating system (Linux, Windows, …) and version?
> Darwin MacBookPro.local 24.6.0 Darwin Kernel Version 24.6.0: Mon Jul 14 11:30:29 PDT 2025; root:xnu-11417.140.69~1/RELEASE_ARM64_T6000 arm64

### What did you do?
```proto
edition = "2024";

package example;
option go_package=".;example";

service Foo {}
```
```bash
protoc  --go_out=. --go-grpc_out=. example.proto
```


### What did you expect to see?
Generate `example.pb.go` and `example_grpc.pb.go`.

### What did you see instead?
> protoc-gen-go-grpc: invalid FileDescriptorProto "example.proto": proto: use of edition EDITION_2024 not yet supported by the Go Protobuf runtime


Hints from issue comments:

Solution patch:
diff --git a/balancer/grpclb/grpc_lb_v1/load_balancer_grpc.pb.go b/balancer/grpclb/grpc_lb_v1/load_balancer_grpc.pb.go
index c4ace87b..f506074e 100644
--- a/balancer/grpclb/grpc_lb_v1/load_balancer_grpc.pb.go
+++ b/balancer/grpclb/grpc_lb_v1/load_balancer_grpc.pb.go
@@ -19,7 +19,7 @@
 
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
-// - protoc-gen-go-grpc v1.5.1
+// - protoc-gen-go-grpc v1.6.0
 // - protoc             v5.27.1
 // source: grpc/lb/v1/load_balancer.proto
 
diff --git a/channelz/grpc_channelz_v1/channelz_grpc.pb.go b/channelz/grpc_channelz_v1/channelz_grpc.pb.go
index 5d244fd6..733f46fb 100644
--- a/channelz/grpc_channelz_v1/channelz_grpc.pb.go
+++ b/channelz/grpc_channelz_v1/channelz_grpc.pb.go
@@ -21,7 +21,7 @@
 
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
-// - protoc-gen-go-grpc v1.5.1
+// - protoc-gen-go-grpc v1.6.0
 // - protoc             v5.27.1
 // source: grpc/channelz/v1/channelz.proto
 
diff --git a/cmd/protoc-gen-go-grpc/main.go b/cmd/protoc-gen-go-grpc/main.go
index 2d0c2666..b3f69223 100644
--- a/cmd/protoc-gen-go-grpc/main.go
+++ b/cmd/protoc-gen-go-grpc/main.go
@@ -42,7 +42,7 @@ import (
 	"google.golang.org/protobuf/types/pluginpb"
 )
 
-const version = "1.5.1"
+const version = "1.6.0"
 
 var requireUnimplemented *bool
 var useGenericStreams *bool
diff --git a/credentials/alts/internal/proto/grpc_gcp/handshaker_grpc.pb.go b/credentials/alts/internal/proto/grpc_gcp/handshaker_grpc.pb.go
index 21cb01be..44b99ce7 100644
--- a/credentials/alts/internal/proto/grpc_gcp/handshaker_grpc.pb.go
+++ b/credentials/alts/internal/proto/grpc_gcp/handshaker_grpc.pb.go
@@ -17,7 +17,7 @@
 
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
-// - protoc-gen-go-grpc v1.5.1
+// - protoc-gen-go-grpc v1.6.0
 // - protoc             v5.27.1
 // source: grpc/gcp/handshaker.proto
 
diff --git a/examples/features/proto/echo/echo_grpc.pb.go b/examples/features/proto/echo/echo_grpc.pb.go
index bd0c1993..75b8cceb 100644
--- a/examples/features/proto/echo/echo_grpc.pb.go
+++ b/examples/features/proto/echo/echo_grpc.pb.go
@@ -17,7 +17,7 @@
 
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
-// - protoc-gen-go-grpc v1.5.1
+// - protoc-gen-go-grpc v1.6.0
 // - protoc             v5.27.1
 // source: examples/features/proto/echo/echo.proto
 
diff --git a/examples/go.mod b/examples/go.mod
index 11fd958c..fdfd7106 100644
--- a/examples/go.mod
+++ b/examples/go.mod
@@ -73,11 +73,11 @@ require (
 	go.opentelemetry.io/otel/metric v1.38.0 // indirect
 	go.opentelemetry.io/otel/trace v1.38.0 // indirect
 	go.yaml.in/yaml/v2 v2.4.3 // indirect
-	golang.org/x/crypto v0.43.0 // indirect
-	golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82 // indirect
-	golang.org/x/sync v0.17.0 // indirect
-	golang.org/x/sys v0.37.0 // indirect
-	golang.org/x/text v0.30.0 // indirect
+	golang.org/x/crypto v0.45.0 // indirect
+	golang.org/x/net v0.47.0 // indirect
+	golang.org/x/sync v0.18.0 // indirect
+	golang.org/x/sys v0.38.0 // indirect
+	golang.org/x/text v0.31.0 // indirect
 	golang.org/x/time v0.14.0 // indirect
 	google.golang.org/api v0.254.0 // indirect
 	google.golang.org/genproto v0.0.0-20251029180050-ab9386a59fda // indirect
diff --git a/examples/go.sum b/examples/go.sum
index 0235a285..41fd075e 100644
--- a/examples/go.sum
+++ b/examples/go.sum
@@ -3919,8 +3919,10 @@ golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632
 golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
 golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
 golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
-golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
 golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
+golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
+golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
+golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
 golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -4026,6 +4028,7 @@ golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
 golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
 golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
 golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
+golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -4127,8 +4130,9 @@ golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
 golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
 golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
 golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
-golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82 h1:6/3JGEh1C88g7m+qzzTbl3A0FtsLguXieqofVLU/JAo=
 golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
+golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
+golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -4212,8 +4216,9 @@ golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
-golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
 golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
+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/sys v0.0.0-20180823144017-11551d06cbcc/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=
@@ -4343,13 +4348,15 @@ golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
-golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
 golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
+golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
 golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
 golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0=
 golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b/go.mod h1:4ZwOYna0/zsOKwuR5X/m0QFOJpSZvAxFfkQT+Erd9D4=
 golang.org/x/telemetry v0.0.0-20250807160809-1a19826ec488/go.mod h1:fGb/2+tgXXjhjHsTNdVEEMZNWA0quBnfrO+AfoDSAKw=
 golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053/go.mod h1:+nZKN+XVh4LCiA9DV3ywrzN4gumyCnKjau3NGb9SGoE=
+golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -4388,6 +4395,7 @@ golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
 golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
 golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
 golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
+golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -4424,8 +4432,9 @@ golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
 golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
 golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
 golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
-golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
 golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
+golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
+golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
 golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -4536,6 +4545,7 @@ golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg
 golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
 golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
 golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
+golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
 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=
diff --git a/examples/helloworld/helloworld/helloworld_grpc.pb.go b/examples/helloworld/helloworld/helloworld_grpc.pb.go
index fbd854ea..be396a90 100644
--- a/examples/helloworld/helloworld/helloworld_grpc.pb.go
+++ b/examples/helloworld/helloworld/helloworld_grpc.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
-// - protoc-gen-go-grpc v1.5.1
+// - protoc-gen-go-grpc v1.6.0
 // - protoc             v5.27.1
 // source: examples/helloworld/helloworld/helloworld.proto
 
diff --git a/examples/route_guide/routeguide/route_guide_grpc.pb.go b/examples/route_guide/routeguide/route_guide_grpc.pb.go
index db5fe607..7d51214b 100644
--- a/examples/route_guide/routeguide/route_guide_grpc.pb.go
+++ b/examples/route_guide/routeguide/route_guide_grpc.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
-// - protoc-gen-go-grpc v1.5.1
+// - protoc-gen-go-grpc v1.6.0
 // - protoc             v5.27.1
 // source: examples/route_guide/routeguide/route_guide.proto
 
diff --git a/gcp/observability/go.mod b/gcp/observability/go.mod
index a4eca8c4..e150e453 100644
--- a/gcp/observability/go.mod
+++ b/gcp/observability/go.mod
@@ -50,11 +50,11 @@ require (
 	go.opentelemetry.io/otel v1.38.0 // indirect
 	go.opentelemetry.io/otel/metric v1.38.0 // indirect
 	go.opentelemetry.io/otel/trace v1.38.0 // indirect
-	golang.org/x/crypto v0.43.0 // indirect
-	golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82 // indirect
-	golang.org/x/sync v0.17.0 // indirect
-	golang.org/x/sys v0.37.0 // indirect
-	golang.org/x/text v0.30.0 // indirect
+	golang.org/x/crypto v0.45.0 // indirect
+	golang.org/x/net v0.47.0 // indirect
+	golang.org/x/sync v0.18.0 // indirect
+	golang.org/x/sys v0.38.0 // indirect
+	golang.org/x/text v0.31.0 // indirect
 	golang.org/x/time v0.14.0 // indirect
 	google.golang.org/genproto v0.0.0-20251029180050-ab9386a59fda // indirect
 	google.golang.org/genproto/googleapis/api v0.0.0-20251029180050-ab9386a59fda // indirect
diff --git a/gcp/observability/go.sum b/gcp/observability/go.sum
index afc211c0..db39e5b6 100644
--- a/gcp/observability/go.sum
+++ b/gcp/observability/go.sum
@@ -3609,8 +3609,9 @@ golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632
 golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
 golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
 golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
-golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
 golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
+golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
+golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
 golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -3814,8 +3815,9 @@ golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
 golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
 golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
 golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
-golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82 h1:6/3JGEh1C88g7m+qzzTbl3A0FtsLguXieqofVLU/JAo=
 golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
+golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
+golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -3895,8 +3897,9 @@ golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
-golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
 golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
+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/sys v0.0.0-20180823144017-11551d06cbcc/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=
@@ -4025,8 +4028,9 @@ golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
-golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
 golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
+golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
 golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
 golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0=
 golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b/go.mod h1:4ZwOYna0/zsOKwuR5X/m0QFOJpSZvAxFfkQT+Erd9D4=
@@ -4104,8 +4108,9 @@ golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
 golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
 golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
 golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
-golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
 golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
+golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
+golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
 golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
diff --git a/health/grpc_health_v1/health_grpc.pb.go b/health/grpc_health_v1/health_grpc.pb.go
index f2c01f29..e99cd5c8 100644
--- a/health/grpc_health_v1/health_grpc.pb.go
+++ b/health/grpc_health_v1/health_grpc.pb.go
@@ -17,7 +17,7 @@
 
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
-// - protoc-gen-go-grpc v1.5.1
+// - protoc-gen-go-grpc v1.6.0
 // - protoc             v5.27.1
 // source: grpc/health/v1/health.proto
 
diff --git a/internal/proto/grpc_lookup_v1/rls_grpc.pb.go b/internal/proto/grpc_lookup_v1/rls_grpc.pb.go
index 16611074..5b340129 100644
--- a/internal/proto/grpc_lookup_v1/rls_grpc.pb.go
+++ b/internal/proto/grpc_lookup_v1/rls_grpc.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
-// - protoc-gen-go-grpc v1.5.1
+// - protoc-gen-go-grpc v1.6.0
 // - protoc             v5.27.1
 // source: grpc/lookup/v1/rls.proto
 
diff --git a/internal/xds/bootstrap/bootstrap.go b/internal/xds/bootstrap/bootstrap.go
index f3960def..79fb7902 100644
--- a/internal/xds/bootstrap/bootstrap.go
+++ b/internal/xds/bootstrap/bootstrap.go
@@ -44,6 +44,7 @@ import (
 
 const (
 	serverFeaturesIgnoreResourceDeletion = "ignore_resource_deletion"
+	serverFeaturesTrustedXDSServer       = "trusted_xds_server"
 	gRPCUserAgentName                    = "gRPC Go"
 	clientFeatureNoOverprovisioning      = "envoy.lb.does_not_support_overprovisioning"
 	clientFeatureResourceWrapper         = "xds.config.resource-in-sotw"
@@ -256,6 +257,18 @@ func (sc *ServerConfig) ServerFeaturesIgnoreResourceDeletion() bool {
 	return false
 }
 
+// ServerFeaturesTrustedXDSServer returns true if this server is trusted,
+// and gRPC should accept security-config-affecting fields from the server
+// as described in gRFC A81.
+func (sc *ServerConfig) ServerFeaturesTrustedXDSServer() bool {
+	for _, sf := range sc.serverFeatures {
+		if sf == serverFeaturesTrustedXDSServer {
+			return true
+		}
+	}
+	return false
+}
+
 // SelectedChannelCreds returns the selected credentials configuration for
 // communicating with this server.
 func (sc *ServerConfig) SelectedChannelCreds() ChannelCreds {
diff --git a/internal/xds/clients/xdsclient/authority.go b/internal/xds/clients/xdsclient/authority.go
index e4425fa7..b8cb78fb 100644
--- a/internal/xds/clients/xdsclient/authority.go
+++ b/internal/xds/clients/xdsclient/authority.go
@@ -470,7 +470,7 @@ func (a *authority) handleADSResourceUpdate(serverConfig *ServerConfig, rType Re
 			// "resource-not-found" error.
 			continue
 		}
-		if serverConfig.IgnoreResourceDeletion {
+		if serverConfig.SupportsServerFeature(ServerFeatureIgnoreResourceDeletion) {
 			// Per A53, resource deletions are ignored if the
 			// `ignore_resource_deletion` server feature is enabled through the
 			// xDS client configuration. If the resource deletion is to be
diff --git a/internal/xds/clients/xdsclient/clientimpl_watchers.go b/internal/xds/clients/xdsclient/clientimpl_watchers.go
index 68b29295..b305b064 100644
--- a/internal/xds/clients/xdsclient/clientimpl_watchers.go
+++ b/internal/xds/clients/xdsclient/clientimpl_watchers.go
@@ -19,6 +19,7 @@
 package xdsclient
 
 import (
+	"context"
 	"fmt"
 
 	"google.golang.org/grpc/internal/xds/clients/xdsclient/internal/xdsresource"
@@ -41,9 +42,11 @@ func (w *wrappingWatcher) ResourceError(err error, done func()) {
 
 // WatchResource starts watching the specified resource.
 //
-// typeURL specifies the resource type implementation to use. The watch fails
-// if there is no resource type implementation for the given typeURL. See the
-// ResourceTypes field in the Config struct used to create the XDSClient.
+// The watch fails to start if:
+//   - There is no ResourceType implementation for the given typeURL in the
+//     ResourceTypes field of the Config struct used to create the XDSClient.
+//   - The provided resourceName contains an authority that is not present in the
+//     Authorities field.
 //
 // The returned function cancels the watch and prevents future calls to the
 // watcher.
@@ -61,8 +64,10 @@ func (c *XDSClient) WatchResource(typeURL, resourceName string, watcher Resource
 
 	rType, ok := c.config.ResourceTypes[typeURL]
 	if !ok {
-		logger.Warningf("ResourceType implementation for resource type url %v is not found", rType.TypeURL)
-		watcher.ResourceError(fmt.Errorf("ResourceType implementation for resource type url %v is not found", rType.TypeURL), func() {})
+		logger.Warningf("ResourceType implementation for resource type url %q is not found", rType.TypeURL)
+		c.serializer.TrySchedule(func(context.Context) {
+			watcher.ResourceError(fmt.Errorf("no ResourceType implementation found for typeURL %q", rType.TypeURL), func() {})
+		})
 		return func() {}
 	}
 
@@ -70,7 +75,9 @@ func (c *XDSClient) WatchResource(typeURL, resourceName string, watcher Resource
 	a := c.getAuthorityForResource(n)
 	if a == nil {
 		logger.Warningf("Watch registered for name %q of type %q, authority %q is not found", rType.TypeName, resourceName, n.Authority)
-		watcher.ResourceError(fmt.Errorf("authority %q not found in bootstrap config for resource %q", n.Authority, resourceName), func() {})
+		c.serializer.TrySchedule(func(context.Context) {
+			watcher.ResourceError(fmt.Errorf("authority %q not found in the config for resource %q", n.Authority, resourceName), func() {})
+		})
 		return func() {}
 	}
 	// The watchResource method on the authority is invoked with n.String()
diff --git a/internal/xds/clients/xdsclient/resource_watcher.go b/internal/xds/clients/xdsclient/resource_watcher.go
index 37d01bc7..15891d9b 100644
--- a/internal/xds/clients/xdsclient/resource_watcher.go
+++ b/internal/xds/clients/xdsclient/resource_watcher.go
@@ -21,6 +21,9 @@ package xdsclient
 // ResourceWatcher is notified of the resource updates and errors that are
 // received by the xDS client from the management server.
 //
+// All methods on this interface are guaranteed to be called serially by the xDS
+// client.
+//
 // All methods contain a done parameter which should be called when processing
 // of the update has completed.  For example, if processing a resource requires
 // watching new resources, those watches should be completed before done is
diff --git a/internal/xds/clients/xdsclient/xdsconfig.go b/internal/xds/clients/xdsclient/xdsconfig.go
index 9d376e50..14d64c1e 100644
--- a/internal/xds/clients/xdsclient/xdsconfig.go
+++ b/internal/xds/clients/xdsclient/xdsconfig.go
@@ -24,6 +24,20 @@ import (
 	"google.golang.org/grpc/internal/xds/clients"
 )
 
+// ServerFeature indicates the features that will be supported by an xDS server.
+type ServerFeature uint64
+
+const (
+	// ServerFeatureIgnoreResourceDeletion indicates that the server supports a
+	// feature where the xDS client can ignore resource deletions from this server,
+	// as described in gRFC A53.
+	ServerFeatureIgnoreResourceDeletion ServerFeature = 1 << iota
+	// ServerFeatureTrustedXDSServer returns true if this server is trusted,
+	// and gRPC should accept security-config-affecting fields from the server
+	// as described in gRFC A81.
+	ServerFeatureTrustedXDSServer
+)
+
 // Config is used to configure an xDS client. After one has been passed to the
 // xDS client's New function, no part of it may be modified. A Config may be
 // reused; the xdsclient package will also not modify it.
@@ -74,17 +88,7 @@ type Config struct {
 // ServerConfig contains configuration for an xDS management server.
 type ServerConfig struct {
 	ServerIdentifier clients.ServerIdentifier
-
-	// IgnoreResourceDeletion is a server feature which if set to true,
-	// indicates that resource deletion errors from xDS management servers can
-	// be ignored and cached resource data can be used.
-	//
-	// This will be removed in the future once we implement gRFC A88
-	// and two new fields FailOnDataErrors and
-	// ResourceTimerIsTransientError will be introduced.
-	IgnoreResourceDeletion bool
-
-	// TODO: Link to gRFC A88
+	ServerFeature    ServerFeature // ServerFeature stores a bitmap of supported features.
 }
 
 // Authority contains configuration for an xDS control plane authority.
@@ -98,5 +102,11 @@ type Authority struct {
 }
 
 func isServerConfigEqual(a, b *ServerConfig) bool {
-	return a.ServerIdentifier == b.ServerIdentifier && a.IgnoreResourceDeletion == b.IgnoreResourceDeletion
+	return a.ServerIdentifier == b.ServerIdentifier && a.ServerFeature == b.ServerFeature
+}
+
+// SupportsServerFeature returns true if the server configuration indicates that
+// the server supports the given feature.
+func (s *ServerConfig) SupportsServerFeature(feature ServerFeature) bool {
+	return s.ServerFeature&feature != 0
 }
diff --git a/internal/xds/matcher/string_matcher.go b/internal/xds/matcher/string_matcher.go
index c138f787..a6cf05dd 100644
--- a/internal/xds/matcher/string_matcher.go
+++ b/internal/xds/matcher/string_matcher.go
@@ -48,24 +48,49 @@ type StringMatcher struct {
 
 // Match returns true if input matches the criteria in the given StringMatcher.
 func (sm StringMatcher) Match(input string) bool {
-	if sm.ignoreCase {
-		input = strings.ToLower(input)
-	}
 	switch {
 	case sm.exactMatch != nil:
+		if sm.ignoreCase {
+			input = strings.ToLower(input)
+		}
 		return input == *sm.exactMatch
 	case sm.prefixMatch != nil:
+		if sm.ignoreCase {
+			input = strings.ToLower(input)
+		}
 		return strings.HasPrefix(input, *sm.prefixMatch)
 	case sm.suffixMatch != nil:
+		if sm.ignoreCase {
+			input = strings.ToLower(input)
+		}
 		return strings.HasSuffix(input, *sm.suffixMatch)
-	case sm.regexMatch != nil:
-		return grpcutil.FullMatchWithRegex(sm.regexMatch, input)
 	case sm.containsMatch != nil:
+		if sm.ignoreCase {
+			input = strings.ToLower(input)
+		}
 		return strings.Contains(input, *sm.containsMatch)
+	case sm.regexMatch != nil:
+		return grpcutil.FullMatchWithRegex(sm.regexMatch, input)
 	}
 	return false
 }
 
+// newStrPtr allocates a new string that holds the value of input and returns a
+// pointer to it. ignoreCase controls if a lower case version of input is used.
+func newStrPtr(input *string, ignoreCase bool) *string {
+	if input == nil {
+		return nil
+	}
+
+	s := new(string)
+	if ignoreCase {
+		*s = strings.ToLower(*input)
+	} else {
+		*s = *input
+	}
+	return s
+}
+
 // StringMatcherFromProto is a helper function to create a StringMatcher from
 // the corresponding StringMatcher proto.
 //
@@ -78,26 +103,17 @@ func StringMatcherFromProto(matcherProto *v3matcherpb.StringMatcher) (StringMatc
 	matcher := StringMatcher{ignoreCase: matcherProto.GetIgnoreCase()}
 	switch mt := matcherProto.GetMatchPattern().(type) {
 	case *v3matcherpb.StringMatcher_Exact:
-		matcher.exactMatch = &mt.Exact
-		if matcher.ignoreCase {
-			*matcher.exactMatch = strings.ToLower(*matcher.exactMatch)
-		}
+		matcher.exactMatch = newStrPtr(&mt.Exact, matcher.ignoreCase)
 	case *v3matcherpb.StringMatcher_Prefix:
 		if matcherProto.GetPrefix() == "" {
 			return StringMatcher{}, errors.New("empty prefix is not allowed in StringMatcher")
 		}
-		matcher.prefixMatch = &mt.Prefix
-		if matcher.ignoreCase {
-			*matcher.prefixMatch = strings.ToLower(*matcher.prefixMatch)
-		}
+		matcher.prefixMatch = newStrPtr(&mt.Prefix, matcher.ignoreCase)
 	case *v3matcherpb.StringMatcher_Suffix:
 		if matcherProto.GetSuffix() == "" {
 			return StringMatcher{}, errors.New("empty suffix is not allowed in StringMatcher")
 		}
-		matcher.suffixMatch = &mt.Suffix
-		if matcher.ignoreCase {
-			*matcher.suffixMatch = strings.ToLower(*matcher.suffixMatch)
-		}
+		matcher.suffixMatch = newStrPtr(&mt.Suffix, matcher.ignoreCase)
 	case *v3matcherpb.StringMatcher_SafeRegex:
 		regex := matcherProto.GetSafeRegex().GetRegex()
 		re, err := regexp.Compile(regex)
@@ -109,40 +125,59 @@ func StringMatcherFromProto(matcherProto *v3matcherpb.StringMatcher) (StringMatc
 		if matcherProto.GetContains() == "" {
 			return StringMatcher{}, errors.New("empty contains is not allowed in StringMatcher")
 		}
-		matcher.containsMatch = &mt.Contains
-		if matcher.ignoreCase {
-			*matcher.containsMatch = strings.ToLower(*matcher.containsMatch)
-		}
+		matcher.containsMatch = newStrPtr(&mt.Contains, matcher.ignoreCase)
 	default:
 		return StringMatcher{}, fmt.Errorf("unrecognized string matcher: %+v", matcherProto)
 	}
 	return matcher, nil
 }
 
-// StringMatcherForTesting is a helper function to create a StringMatcher based
-// on the given arguments. Intended only for testing purposes.
-func StringMatcherForTesting(exact, prefix, suffix, contains *string, regex *regexp.Regexp, ignoreCase bool) StringMatcher {
-	sm := StringMatcher{
-		exactMatch:    exact,
-		prefixMatch:   prefix,
-		suffixMatch:   suffix,
-		regexMatch:    regex,
-		containsMatch: contains,
+// NewExactStringMatcher creates a string matcher that requires the input string
+// to exactly match the pattern specified here. The match will be case
+// insensitive if ignore_case is true.
+func NewExactStringMatcher(pattern string, ignoreCase bool) StringMatcher {
+	return StringMatcher{
+		exactMatch: newStrPtr(&pattern, ignoreCase),
+		ignoreCase: ignoreCase,
+	}
+}
+
+// NewPrefixStringMatcher creates a string matcher that requires the input
+// string to contain the prefix specified here. The match will be case
+// insensitive if ignore_case is true.
+func NewPrefixStringMatcher(prefix string, ignoreCase bool) StringMatcher {
+	return StringMatcher{
+		prefixMatch: newStrPtr(&prefix, ignoreCase),
+		ignoreCase:  ignoreCase,
+	}
+}
+
+// NewSuffixStringMatcher creates a string matcher that requires the input
+// string to contain the suffix specified here. The match will be case
+// insensitive if ignore_case is true.
+func NewSuffixStringMatcher(suffix string, ignoreCase bool) StringMatcher {
+	return StringMatcher{
+		suffixMatch: newStrPtr(&suffix, ignoreCase),
+		ignoreCase:  ignoreCase,
+	}
+}
+
+// NewContainsStringMatcher creates a string matcher that requires the input
+// string to contain the pattern specified here. The match will be case
+// insensitive if ignore_case is true.
+func NewContainsStringMatcher(pattern string, ignoreCase bool) StringMatcher {
+	return StringMatcher{
+		containsMatch: newStrPtr(&pattern, ignoreCase),
 		ignoreCase:    ignoreCase,
 	}
-	if ignoreCase {
-		switch {
-		case sm.exactMatch != nil:
-			*sm.exactMatch = strings.ToLower(*exact)
-		case sm.prefixMatch != nil:
-			*sm.prefixMatch = strings.ToLower(*prefix)
-		case sm.suffixMatch != nil:
-			*sm.suffixMatch = strings.ToLower(*suffix)
-		case sm.containsMatch != nil:
-			*sm.containsMatch = strings.ToLower(*contains)
-		}
+}
+
+// NewRegexStringMatcher creates a string matcher that requires the input string
+// to match the regular expression specified here.
+func NewRegexStringMatcher(regex *regexp.Regexp) StringMatcher {
+	return StringMatcher{
+		regexMatch: regex,
 	}
-	return sm
 }
 
 // ExactMatch returns the value of the configured exact match or an empty string
diff --git a/internal/xds/xdsclient/clientimpl.go b/internal/xds/xdsclient/clientimpl.go
index f2325e0e..b2088647 100644
--- a/internal/xds/xdsclient/clientimpl.go
+++ b/internal/xds/xdsclient/clientimpl.go
@@ -162,6 +162,32 @@ func (c *clientImpl) decrRef() int32 {
 	return atomic.AddInt32(&c.refCount, -1)
 }
 
+func buildServerConfigs(bootstrapSC []*bootstrap.ServerConfig, grpcTransportConfigs map[string]grpctransport.Config, gServerCfgMap map[xdsclient.ServerConfig]*bootstrap.ServerConfig) ([]xdsclient.ServerConfig, error) {
+	var gServerCfg []xdsclient.ServerConfig
+	for _, sc := range bootstrapSC {
+		if err := populateGRPCTransportConfigsFromServerConfig(sc, grpcTransportConfigs); err != nil {
+			return nil, err
+		}
+		var serverFeatures xdsclient.ServerFeature
+		if sc.ServerFeaturesIgnoreResourceDeletion() {
+			serverFeatures = serverFeatures | xdsclient.ServerFeatureIgnoreResourceDeletion
+		}
+		if sc.ServerFeaturesTrustedXDSServer() {
+			serverFeatures = serverFeatures | xdsclient.ServerFeatureTrustedXDSServer
+		}
+		gsc := xdsclient.ServerConfig{
+			ServerIdentifier: clients.ServerIdentifier{
+				ServerURI:  sc.ServerURI(),
+				Extensions: grpctransport.ServerIdentifierExtension{ConfigName: sc.SelectedChannelCreds().Type},
+			},
+			ServerFeature: serverFeatures,
+		}
+		gServerCfg = append(gServerCfg, gsc)
+		gServerCfgMap[gsc] = sc
+	}
+	return gServerCfg, nil
+}
+
 // buildXDSClientConfig builds the xdsclient.Config from the bootstrap.Config.
 func buildXDSClientConfig(config *bootstrap.Config, metricsRecorder estats.MetricsRecorder, target string, watchExpiryTimeout time.Duration) (xdsclient.Config, error) {
 	grpcTransportConfigs := make(map[string]grpctransport.Config)
@@ -175,30 +201,16 @@ func buildXDSClientConfig(config *bootstrap.Config, metricsRecorder estats.Metri
 		if len(cfg.XDSServers) >= 1 {
 			serverCfg = cfg.XDSServers
 		}
-		var gServerCfg []xdsclient.ServerConfig
-		for _, sc := range serverCfg {
-			if err := populateGRPCTransportConfigsFromServerConfig(sc, grpcTransportConfigs); err != nil {
-				return xdsclient.Config{}, err
-			}
-			gsc := xdsclient.ServerConfig{
-				ServerIdentifier:       clients.ServerIdentifier{ServerURI: sc.ServerURI(), Extensions: grpctransport.ServerIdentifierExtension{ConfigName: sc.SelectedChannelCreds().Type}},
-				IgnoreResourceDeletion: sc.ServerFeaturesIgnoreResourceDeletion()}
-			gServerCfg = append(gServerCfg, gsc)
-			gServerCfgMap[gsc] = sc
+		gsc, err := buildServerConfigs(serverCfg, grpcTransportConfigs, gServerCfgMap)
+		if err != nil {
+			return xdsclient.Config{}, err
 		}
-		gAuthorities[name] = xdsclient.Authority{XDSServers: gServerCfg}
+		gAuthorities[name] = xdsclient.Authority{XDSServers: gsc}
 	}
 
-	gServerCfgs := make([]xdsclient.ServerConfig, 0, len(config.XDSServers()))
-	for _, sc := range config.XDSServers() {
-		if err := populateGRPCTransportConfigsFromServerConfig(sc, grpcTransportConfigs); err != nil {
-			return xdsclient.Config{}, err
-		}
-		gsc := xdsclient.ServerConfig{
-			ServerIdentifier:       clients.ServerIdentifier{ServerURI: sc.ServerURI(), Extensions: grpctransport.ServerIdentifierExtension{ConfigName: sc.SelectedChannelCreds().Type}},
-			IgnoreResourceDeletion: sc.ServerFeaturesIgnoreResourceDeletion()}
-		gServerCfgs = append(gServerCfgs, gsc)
-		gServerCfgMap[gsc] = sc
+	gServerCfgs, err := buildServerConfigs(config.XDSServers(), grpcTransportConfigs, gServerCfgMap)
+	if err != nil {
+		return xdsclient.Config{}, err
 	}
 
 	node := config.Node()
diff --git a/interop/observability/go.mod b/interop/observability/go.mod
index 77c8babb..d0d15c78 100644
--- a/interop/observability/go.mod
+++ b/interop/observability/go.mod
@@ -49,12 +49,12 @@ require (
 	go.opentelemetry.io/otel v1.38.0 // indirect
 	go.opentelemetry.io/otel/metric v1.38.0 // indirect
 	go.opentelemetry.io/otel/trace v1.38.0 // indirect
-	golang.org/x/crypto v0.43.0 // indirect
-	golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82 // indirect
+	golang.org/x/crypto v0.45.0 // indirect
+	golang.org/x/net v0.47.0 // indirect
 	golang.org/x/oauth2 v0.32.0 // indirect
-	golang.org/x/sync v0.17.0 // indirect
-	golang.org/x/sys v0.37.0 // indirect
-	golang.org/x/text v0.30.0 // indirect
+	golang.org/x/sync v0.18.0 // indirect
+	golang.org/x/sys v0.38.0 // indirect
+	golang.org/x/text v0.31.0 // indirect
 	golang.org/x/time v0.14.0 // indirect
 	google.golang.org/api v0.254.0 // indirect
 	google.golang.org/genproto v0.0.0-20251029180050-ab9386a59fda // indirect
diff --git a/interop/observability/go.sum b/interop/observability/go.sum
index 4782e392..e885d265 100644
--- a/interop/observability/go.sum
+++ b/interop/observability/go.sum
@@ -3889,8 +3889,10 @@ golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632
 golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
 golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
 golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
-golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
 golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
+golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
+golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
+golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
 golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -3996,6 +3998,7 @@ golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
 golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
 golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
 golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
+golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -4097,8 +4100,9 @@ golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
 golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
 golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
 golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
-golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82 h1:6/3JGEh1C88g7m+qzzTbl3A0FtsLguXieqofVLU/JAo=
 golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
+golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
+golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -4182,8 +4186,9 @@ golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
 golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
-golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
 golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
+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/sys v0.0.0-20180823144017-11551d06cbcc/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=
@@ -4313,13 +4318,15 @@ golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
-golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
 golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
+golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
 golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
 golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0=
 golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b/go.mod h1:4ZwOYna0/zsOKwuR5X/m0QFOJpSZvAxFfkQT+Erd9D4=
 golang.org/x/telemetry v0.0.0-20250807160809-1a19826ec488/go.mod h1:fGb/2+tgXXjhjHsTNdVEEMZNWA0quBnfrO+AfoDSAKw=
 golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053/go.mod h1:+nZKN+XVh4LCiA9DV3ywrzN4gumyCnKjau3NGb9SGoE=
+golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -4358,6 +4365,7 @@ golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
 golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
 golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
 golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
+golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -4394,8 +4402,9 @@ golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
 golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
 golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
 golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
-golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
 golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
+golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
+golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
 golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -4506,6 +4515,7 @@ golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg
 golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
 golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
 golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
+golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
 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=
diff --git a/profiling/proto/service_grpc.pb.go b/profiling/proto/service_grpc.pb.go
index fd3696d2..304b6c14 100644
--- a/profiling/proto/service_grpc.pb.go
+++ b/profiling/proto/service_grpc.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
-// - protoc-gen-go-grpc v1.5.1
+// - protoc-gen-go-grpc v1.6.0
 // - protoc             v5.27.1
 // source: profiling/proto/service.proto
 
diff --git a/reflection/grpc_reflection_v1/reflection_grpc.pb.go b/reflection/grpc_reflection_v1/reflection_grpc.pb.go
index f4a361c6..93a24363 100644
--- a/reflection/grpc_reflection_v1/reflection_grpc.pb.go
+++ b/reflection/grpc_reflection_v1/reflection_grpc.pb.go
@@ -21,7 +21,7 @@
 
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
-// - protoc-gen-go-grpc v1.5.1
+// - protoc-gen-go-grpc v1.6.0
 // - protoc             v5.27.1
 // source: grpc/reflection/v1/reflection.proto
 
diff --git a/reflection/grpc_reflection_v1alpha/reflection_grpc.pb.go b/reflection/grpc_reflection_v1alpha/reflection_grpc.pb.go
index 0a43b521..cee004ab 100644
--- a/reflection/grpc_reflection_v1alpha/reflection_grpc.pb.go
+++ b/reflection/grpc_reflection_v1alpha/reflection_grpc.pb.go
@@ -18,7 +18,7 @@
 
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
-// - protoc-gen-go-grpc v1.5.1
+// - protoc-gen-go-grpc v1.6.0
 // - protoc             v5.27.1
 // grpc/reflection/v1alpha/reflection.proto is a deprecated file.
 
diff --git a/security/advancedtls/go.mod b/security/advancedtls/go.mod
index e788bdca..44c9d396 100644
--- a/security/advancedtls/go.mod
+++ b/security/advancedtls/go.mod
@@ -4,7 +4,7 @@ go 1.24.0
 
 require (
 	github.com/google/go-cmp v0.7.0
-	golang.org/x/crypto v0.43.0
+	golang.org/x/crypto v0.45.0
 	google.golang.org/grpc v1.76.0
 	google.golang.org/grpc/examples v0.0.0-20250407062114-b368379ef8f6
 )
@@ -12,9 +12,9 @@ require (
 require (
 	github.com/go-jose/go-jose/v4 v4.1.3 // indirect
 	github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect
-	golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82 // indirect
-	golang.org/x/sys v0.37.0 // indirect
-	golang.org/x/text v0.30.0 // indirect
+	golang.org/x/net v0.47.0 // indirect
+	golang.org/x/sys v0.38.0 // indirect
+	golang.org/x/text v0.31.0 // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda // indirect
 	google.golang.org/protobuf v1.36.10 // indirect
 )
diff --git a/security/advancedtls/go.sum b/security/advancedtls/go.sum
index 684feb52..522964a0 100644
--- a/security/advancedtls/go.sum
+++ b/security/advancedtls/go.sum
@@ -30,14 +30,14 @@ go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6
 go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
 go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
 go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
-golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
-golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
-golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82 h1:6/3JGEh1C88g7m+qzzTbl3A0FtsLguXieqofVLU/JAo=
-golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
-golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
-golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
-golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
-golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
+golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
+golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
+golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
+golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
+golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
+golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
+golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
 gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
 gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda h1:i/Q+bfisr7gq6feoJnS/DlpdwEL4ihp41fvRiM3Ork0=



Test patch:
diff --git a/credentials/xds/xds_client_test.go b/credentials/xds/xds_client_test.go
index e013184f..cb316ec6 100644
--- a/credentials/xds/xds_client_test.go
+++ b/credentials/xds/xds_client_test.go
@@ -226,7 +226,7 @@ func newTestContextWithHandshakeInfo(parent context.Context, root, identity cert
 	// NewSubConn().
 	var sms []matcher.StringMatcher
 	if sanExactMatch != "" {
-		sms = []matcher.StringMatcher{matcher.StringMatcherForTesting(newStringP(sanExactMatch), nil, nil, nil, nil, false)}
+		sms = []matcher.StringMatcher{matcher.NewExactStringMatcher(sanExactMatch, false)}
 	}
 	info := xdsinternal.NewHandshakeInfo(root, identity, sms, false)
 	uPtr := unsafe.Pointer(info)
@@ -542,7 +542,7 @@ func (s) TestClientCredsProviderSwitch(t *testing.T) {
 	// Create a root provider which will fail the handshake because it does not
 	// use the correct trust roots.
 	root1 := makeRootProvider(t, "x509/client_ca_cert.pem")
-	handshakeInfo := xdsinternal.NewHandshakeInfo(root1, nil, []matcher.StringMatcher{matcher.StringMatcherForTesting(newStringP(defaultTestCertSAN), nil, nil, nil, nil, false)}, false)
+	handshakeInfo := xdsinternal.NewHandshakeInfo(root1, nil, []matcher.StringMatcher{matcher.NewExactStringMatcher(defaultTestCertSAN, false)}, false)
 	// We need to repeat most of what newTestContextWithHandshakeInfo() does
 	// here because we need access to the underlying HandshakeInfo so that we
 	// can update it before the next call to ClientHandshake().
@@ -568,7 +568,7 @@ func (s) TestClientCredsProviderSwitch(t *testing.T) {
 	// Create a new root provider which uses the correct trust roots. And update
 	// the HandshakeInfo with the new provider.
 	root2 := makeRootProvider(t, "x509/server_ca_cert.pem")
-	handshakeInfo = xdsinternal.NewHandshakeInfo(root2, nil, []matcher.StringMatcher{matcher.StringMatcherForTesting(newStringP(defaultTestCertSAN), nil, nil, nil, nil, false)}, false)
+	handshakeInfo = xdsinternal.NewHandshakeInfo(root2, nil, []matcher.StringMatcher{matcher.NewExactStringMatcher(defaultTestCertSAN, false)}, false)
 	// Update the existing pointer, which address attribute will continue to
 	// point to.
 	atomic.StorePointer(&uPtr, unsafe.Pointer(handshakeInfo))
@@ -596,7 +596,3 @@ func (s) TestClientClone(t *testing.T) {
 		t.Fatal("return value from Clone() doesn't point to new credentials instance")
 	}
 }
-
-func newStringP(s string) *string {
-	return &s
-}
diff --git a/internal/credentials/xds/handshake_info_test.go b/internal/credentials/xds/handshake_info_test.go
index 53b2700b..7577813e 100644
--- a/internal/credentials/xds/handshake_info_test.go
+++ b/internal/credentials/xds/handshake_info_test.go
@@ -170,40 +170,40 @@ func TestMatchingSANExists_FailureCases(t *testing.T) {
 		{
 			desc: "exact match",
 			sanMatchers: []matcher.StringMatcher{
-				matcher.StringMatcherForTesting(newStringP("abcd.test.com"), nil, nil, nil, nil, false),
-				matcher.StringMatcherForTesting(newStringP("http://golang"), nil, nil, nil, nil, false),
-				matcher.StringMatcherForTesting(newStringP("HTTP://GOLANG.ORG"), nil, nil, nil, nil, false),
+				matcher.NewExactStringMatcher("abcd.test.com", false),
+				matcher.NewExactStringMatcher("http://golang", false),
+				matcher.NewExactStringMatcher("HTTP://GOLANG.ORG", false),
 			},
 		},
 		{
 			desc: "prefix match",
 			sanMatchers: []matcher.StringMatcher{
-				matcher.StringMatcherForTesting(nil, newStringP("i-aint-the-one"), nil, nil, nil, false),
-				matcher.StringMatcherForTesting(nil, newStringP("192.168.1.1"), nil, nil, nil, false),
-				matcher.StringMatcherForTesting(nil, newStringP("FOO.BAR"), nil, nil, nil, false),
+				matcher.NewPrefixStringMatcher("i-aint-the-one", false),
+				matcher.NewPrefixStringMatcher("192.168.1.1", false),
+				matcher.NewPrefixStringMatcher("FOO.BAR", false),
 			},
 		},
 		{
 			desc: "suffix match",
 			sanMatchers: []matcher.StringMatcher{
-				matcher.StringMatcherForTesting(nil, nil, newStringP("i-aint-the-one"), nil, nil, false),
-				matcher.StringMatcherForTesting(nil, nil, newStringP("1::68"), nil, nil, false),
-				matcher.StringMatcherForTesting(nil, nil, newStringP(".COM"), nil, nil, false),
+				matcher.NewSuffixStringMatcher("i-aint-the-one", false),
+				matcher.NewSuffixStringMatcher("1::68", false),
+				matcher.NewSuffixStringMatcher(".COM", false),
 			},
 		},
 		{
 			desc: "regex match",
 			sanMatchers: []matcher.StringMatcher{
-				matcher.StringMatcherForTesting(nil, nil, nil, nil, regexp.MustCompile(`.*\.examples\.com`), false),
-				matcher.StringMatcherForTesting(nil, nil, nil, nil, regexp.MustCompile(`192\.[0-9]{1,3}\.1\.1`), false),
+				matcher.NewRegexStringMatcher(regexp.MustCompile(`.*\.examples\.com`)),
+				matcher.NewRegexStringMatcher(regexp.MustCompile(`192\.[0-9]{1,3}\.1\.1`)),
 			},
 		},
 		{
 			desc: "contains match",
 			sanMatchers: []matcher.StringMatcher{
-				matcher.StringMatcherForTesting(nil, nil, nil, newStringP("i-aint-the-one"), nil, false),
-				matcher.StringMatcherForTesting(nil, nil, nil, newStringP("2001:db8:1:1::68"), nil, false),
-				matcher.StringMatcherForTesting(nil, nil, nil, newStringP("GRPC"), nil, false),
+				matcher.NewContainsStringMatcher("i-aint-the-one", false),
+				matcher.NewContainsStringMatcher("2001:db8:1:1::68", false),
+				matcher.NewContainsStringMatcher("GRPC", false),
 			},
 		},
 	}
@@ -248,65 +248,65 @@ func TestMatchingSANExists_Success(t *testing.T) {
 		{
 			desc: "exact match dns wildcard",
 			sanMatchers: []matcher.StringMatcher{
-				matcher.StringMatcherForTesting(nil, newStringP("192.168.1.1"), nil, nil, nil, false),
-				matcher.StringMatcherForTesting(newStringP("https://github.com/grpc/grpc-java"), nil, nil, nil, nil, false),
-				matcher.StringMatcherForTesting(newStringP("abc.example.com"), nil, nil, nil, nil, false),
+				matcher.NewPrefixStringMatcher("192.168.1.1", false),
+				matcher.NewExactStringMatcher("https://github.com/grpc/grpc-java", false),
+				matcher.NewExactStringMatcher("abc.example.com", false),
 			},
 		},
 		{
 			desc: "exact match ignore case",
 			sanMatchers: []matcher.StringMatcher{
-				matcher.StringMatcherForTesting(newStringP("FOOBAR@EXAMPLE.COM"), nil, nil, nil, nil, true),
+				matcher.NewExactStringMatcher("FOOBAR@EXAMPLE.COM", true),
 			},
 		},
 		{
 			desc: "prefix match",
 			sanMatchers: []matcher.StringMatcher{
-				matcher.StringMatcherForTesting(nil, nil, newStringP(".co.in"), nil, nil, false),
-				matcher.StringMatcherForTesting(nil, newStringP("192.168.1.1"), nil, nil, nil, false),
-				matcher.StringMatcherForTesting(nil, newStringP("baz.test"), nil, nil, nil, false),
+				matcher.NewSuffixStringMatcher(".co.in", false),
+				matcher.NewPrefixStringMatcher("192.168.1.1", false),
+				matcher.NewPrefixStringMatcher("baz.test", false),
 			},
 		},
 		{
 			desc: "prefix match ignore case",
 			sanMatchers: []matcher.StringMatcher{
-				matcher.StringMatcherForTesting(nil, newStringP("BAZ.test"), nil, nil, nil, true),
+				matcher.NewPrefixStringMatcher("BAZ.test", true),
 			},
 		},
 		{
 			desc: "suffix  match",
 			sanMatchers: []matcher.StringMatcher{
-				matcher.StringMatcherForTesting(nil, nil, nil, nil, regexp.MustCompile(`192\.[0-9]{1,3}\.1\.1`), false),
-				matcher.StringMatcherForTesting(nil, nil, newStringP("192.168.1.1"), nil, nil, false),
-				matcher.StringMatcherForTesting(nil, nil, newStringP("@test.com"), nil, nil, false),
+				matcher.NewRegexStringMatcher(regexp.MustCompile(`192\.[0-9]{1,3}\.1\.1`)),
+				matcher.NewSuffixStringMatcher("192.168.1.1", false),
+				matcher.NewSuffixStringMatcher("@test.com", false),
 			},
 		},
 		{
 			desc: "suffix  match ignore case",
 			sanMatchers: []matcher.StringMatcher{
-				matcher.StringMatcherForTesting(nil, nil, newStringP("@test.COM"), nil, nil, true),
+				matcher.NewSuffixStringMatcher("@test.COM", true),
 			},
 		},
 		{
 			desc: "regex match",
 			sanMatchers: []matcher.StringMatcher{
-				matcher.StringMatcherForTesting(nil, nil, nil, newStringP("https://github.com/grpc/grpc-java"), nil, false),
-				matcher.StringMatcherForTesting(nil, nil, nil, nil, regexp.MustCompile(`192\.[0-9]{1,3}\.1\.1`), false),
-				matcher.StringMatcherForTesting(nil, nil, nil, nil, regexp.MustCompile(`.*\.test\.com`), false),
+				matcher.NewContainsStringMatcher("https://github.com/grpc/grpc-java", false),
+				matcher.NewRegexStringMatcher(regexp.MustCompile(`192\.[0-9]{1,3}\.1\.1`)),
+				matcher.NewRegexStringMatcher(regexp.MustCompile(`.*\.test\.com`)),
 			},
 		},
 		{
 			desc: "contains match",
 			sanMatchers: []matcher.StringMatcher{
-				matcher.StringMatcherForTesting(newStringP("https://github.com/grpc/grpc-java"), nil, nil, nil, nil, false),
-				matcher.StringMatcherForTesting(nil, nil, nil, newStringP("2001:68::db8"), nil, false),
-				matcher.StringMatcherForTesting(nil, nil, nil, newStringP("192.0.0"), nil, false),
+				matcher.NewExactStringMatcher("https://github.com/grpc/grpc-java", false),
+				matcher.NewContainsStringMatcher("2001:68::db8", false),
+				matcher.NewContainsStringMatcher("192.0.0", false),
 			},
 		},
 		{
 			desc: "contains match ignore case",
 			sanMatchers: []matcher.StringMatcher{
-				matcher.StringMatcherForTesting(nil, nil, nil, newStringP("GRPC"), nil, true),
+				matcher.NewContainsStringMatcher("GRPC", true),
 			},
 		},
 	}
@@ -322,10 +322,6 @@ func TestMatchingSANExists_Success(t *testing.T) {
 	}
 }
 
-func newStringP(s string) *string {
-	return &s
-}
-
 func TestEqual(t *testing.T) {
 	tests := []struct {
 		desc      string
@@ -354,31 +350,31 @@ func TestEqual(t *testing.T) {
 		{
 			desc: "same providers, same SAN matchers",
 			hi1: NewHandshakeInfo(testCertProvider{}, testCertProvider{}, []matcher.StringMatcher{
-				matcher.StringMatcherForTesting(newStringP("foo.com"), nil, nil, nil, nil, false),
+				matcher.NewExactStringMatcher("foo.com", false),
 			}, false),
 			hi2: NewHandshakeInfo(testCertProvider{}, testCertProvider{}, []matcher.StringMatcher{
-				matcher.StringMatcherForTesting(newStringP("foo.com"), nil, nil, nil, nil, false),
+				matcher.NewExactStringMatcher("foo.com", false),
 			}, false),
 			wantMatch: true,
 		},
 		{
 			desc: "same providers, different SAN matchers",
 			hi1: NewHandshakeInfo(testCertProvider{}, testCertProvider{}, []matcher.StringMatcher{
-				matcher.StringMatcherForTesting(newStringP("foo.com"), nil, nil, nil, nil, false),
+				matcher.NewExactStringMatcher("foo.com", false),
 			}, false),
 			hi2: NewHandshakeInfo(testCertProvider{}, testCertProvider{}, []matcher.StringMatcher{
-				matcher.StringMatcherForTesting(newStringP("bar.com"), nil, nil, nil, nil, false),
+				matcher.NewExactStringMatcher("bar.com", false),
 			}, false),
 			wantMatch: false,
 		},
 		{
 			desc: "same SAN matchers with different content",
 			hi1: NewHandshakeInfo(&testCertProvider{}, &testCertProvider{}, []matcher.StringMatcher{
-				matcher.StringMatcherForTesting(newStringP("foo.com"), nil, nil, nil, nil, false),
+				matcher.NewExactStringMatcher("foo.com", false),
 			}, false),
 			hi2: NewHandshakeInfo(&testCertProvider{}, &testCertProvider{}, []matcher.StringMatcher{
-				matcher.StringMatcherForTesting(newStringP("foo.com"), nil, nil, nil, nil, false),
-				matcher.StringMatcherForTesting(newStringP("bar.com"), nil, nil, nil, nil, false),
+				matcher.NewExactStringMatcher("foo.com", false),
+				matcher.NewExactStringMatcher("bar.com", false),
 			}, false),
 			wantMatch: false,
 		},
diff --git a/internal/xds/bootstrap/bootstrap_test.go b/internal/xds/bootstrap/bootstrap_test.go
index 4d5de6cc..0dd87416 100644
--- a/internal/xds/bootstrap/bootstrap_test.go
+++ b/internal/xds/bootstrap/bootstrap_test.go
@@ -267,6 +267,22 @@ var (
 				"server_features" : ["xds_v3"]
 			}]
 		}`,
+		"serverSupportsTrustedXDSServer": `
+		{
+			"node": {
+				"id": "ENVOY_NODE_ID",
+				"metadata": {
+				    "TRAFFICDIRECTOR_GRPC_HOSTNAME": "trafficdirector"
+			    }
+			},
+			"xds_servers" : [{
+				"server_uri": "trafficdirector.googleapis.com:443",
+				"channel_creds": [
+					{ "type": "google_default" }
+				],
+				"server_features" : ["trusted_xds_server", "xds_v3"]
+			}]
+		}`,
 	}
 	metadata = &structpb.Struct{
 		Fields: map[string]*structpb.Value{
@@ -338,6 +354,16 @@ var (
 		node: v3Node,
 		clientDefaultListenerResourceNameTemplate: "%s",
 	}
+	configWithGoogleDefaultCredsAndTrustedXDSServer = &Config{
+		xDSServers: []*ServerConfig{{
+			serverURI:            "trafficdirector.googleapis.com:443",
+			channelCreds:         []ChannelCreds{{Type: "google_default"}},
+			serverFeatures:       []string{"trusted_xds_server", "xds_v3"},
+			selectedChannelCreds: ChannelCreds{Type: "google_default"},
+		}},
+		node: v3Node,
+		clientDefaultListenerResourceNameTemplate: "%s",
+	}
 	configWithGoogleDefaultCredsAndNoServerFeatures = &Config{
 		xDSServers: []*ServerConfig{{
 			serverURI:            "trafficdirector.googleapis.com:443",
@@ -539,6 +565,7 @@ func (s) TestGetConfiguration_Success(t *testing.T) {
 		{"goodBootstrap", configWithGoogleDefaultCredsAndV3},
 		{"multipleXDSServers", configWithMultipleServers},
 		{"serverSupportsIgnoreResourceDeletion", configWithGoogleDefaultCredsAndIgnoreResourceDeletion},
+		{"serverSupportsTrustedXDSServer", configWithGoogleDefaultCredsAndTrustedXDSServer},
 		{"istioStyleInsecureWithoutCallCreds", configWithIstioStyleNoCallCreds},
 	}
 
diff --git a/internal/xds/matcher/string_matcher_test.go b/internal/xds/matcher/string_matcher_test.go
index 9528b57e..df76af27 100644
--- a/internal/xds/matcher/string_matcher_test.go
+++ b/internal/xds/matcher/string_matcher_test.go
@@ -130,6 +130,19 @@ func TestStringMatcherFromProto(t *testing.T) {
 			},
 			wantMatcher: StringMatcher{regexMatch: regexp.MustCompile("good?regex?")},
 		},
+		{
+			desc: "regex with ignore case",
+			inputProto: &v3matcherpb.StringMatcher{
+				MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{
+					SafeRegex: &v3matcherpb.RegexMatcher{Regex: "good?regex?"},
+				},
+				IgnoreCase: true,
+			},
+			wantMatcher: StringMatcher{
+				regexMatch: regexp.MustCompile("good?regex?"),
+				ignoreCase: true,
+			},
+		},
 		{
 			desc: "happy case contains",
 			inputProto: &v3matcherpb.StringMatcher{
@@ -165,27 +178,15 @@ func TestStringMatcherFromProto(t *testing.T) {
 
 func TestMatch(t *testing.T) {
 	var (
-		exactMatcher, _           = StringMatcherFromProto(&v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "exact"}})
-		prefixMatcher, _          = StringMatcherFromProto(&v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: "prefix"}})
-		suffixMatcher, _          = StringMatcherFromProto(&v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: "suffix"}})
-		regexMatcher, _           = StringMatcherFromProto(&v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_SafeRegex{SafeRegex: &v3matcherpb.RegexMatcher{Regex: "good?regex?"}}})
-		containsMatcher, _        = StringMatcherFromProto(&v3matcherpb.StringMatcher{MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: "contains"}})
-		exactMatcherIgnoreCase, _ = StringMatcherFromProto(&v3matcherpb.StringMatcher{
-			MatchPattern: &v3matcherpb.StringMatcher_Exact{Exact: "exact"},
-			IgnoreCase:   true,
-		})
-		prefixMatcherIgnoreCase, _ = StringMatcherFromProto(&v3matcherpb.StringMatcher{
-			MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: "prefix"},
-			IgnoreCase:   true,
-		})
-		suffixMatcherIgnoreCase, _ = StringMatcherFromProto(&v3matcherpb.StringMatcher{
-			MatchPattern: &v3matcherpb.StringMatcher_Suffix{Suffix: "suffix"},
-			IgnoreCase:   true,
-		})
-		containsMatcherIgnoreCase, _ = StringMatcherFromProto(&v3matcherpb.StringMatcher{
-			MatchPattern: &v3matcherpb.StringMatcher_Contains{Contains: "contains"},
-			IgnoreCase:   true,
-		})
+		exactMatcher              = NewExactStringMatcher("exact", false)
+		exactMatcherIgnoreCase    = NewExactStringMatcher("exact", true)
+		prefixMatcher             = NewPrefixStringMatcher("prefix", false)
+		prefixMatcherIgnoreCase   = NewPrefixStringMatcher("prefix", true)
+		suffixMatcher             = NewSuffixStringMatcher("suffix", false)
+		suffixMatcherIgnoreCase   = NewSuffixStringMatcher("suffix", true)
+		containsMatcher           = NewContainsStringMatcher("contains", false)
+		containsMatcherIgnoreCase = NewContainsStringMatcher("contains", true)
+		regexMatcher              = NewRegexStringMatcher(regexp.MustCompile("good?regex?"))
 	)
 
 	tests := []struct {
diff --git a/internal/xds/xdsclient/clientimpl_test.go b/internal/xds/xdsclient/clientimpl_test.go
index c2a4d637..3b6a2995 100644
--- a/internal/xds/xdsclient/clientimpl_test.go
+++ b/internal/xds/xdsclient/clientimpl_test.go
@@ -146,7 +146,78 @@ func (s) TestBuildXDSClientConfig_Success(t *testing.T) {
 			}`, testXDSServerURL, testNodeID)),
 			wantXDSClientConfig: func(c *bootstrap.Config) xdsclient.Config {
 				node, serverCfg := c.Node(), c.XDSServers()[0]
-				expectedServer := xdsclient.ServerConfig{ServerIdentifier: clients.ServerIdentifier{ServerURI: serverCfg.ServerURI(), Extensions: grpctransport.ServerIdentifierExtension{ConfigName: "insecure"}}, IgnoreResourceDeletion: true}
+				serverFeature := xdsclient.ServerFeatureIgnoreResourceDeletion
+				expectedServer := xdsclient.ServerConfig{
+					ServerIdentifier: clients.ServerIdentifier{ServerURI: serverCfg.ServerURI(), Extensions: grpctransport.ServerIdentifierExtension{ConfigName: "insecure"}},
+					ServerFeature:    serverFeature}
+				gServerCfgMap := map[xdsclient.ServerConfig]*bootstrap.ServerConfig{expectedServer: serverCfg}
+				return xdsclient.Config{
+					Servers:     []xdsclient.ServerConfig{expectedServer},
+					Node:        clients.Node{ID: node.GetId(), Cluster: node.GetCluster(), Metadata: node.Metadata, UserAgentName: node.UserAgentName, UserAgentVersion: node.GetUserAgentVersion()},
+					Authorities: map[string]xdsclient.Authority{},
+					ResourceTypes: map[string]xdsclient.ResourceType{
+						version.V3ListenerURL:    {TypeURL: version.V3ListenerURL, TypeName: xdsresource.ListenerResourceTypeName, AllResourcesRequiredInSotW: true, Decoder: xdsresource.NewListenerResourceTypeDecoder(c)},
+						version.V3RouteConfigURL: {TypeURL: version.V3RouteConfigURL, TypeName: xdsresource.RouteConfigTypeName, AllResourcesRequiredInSotW: false, Decoder: xdsresource.NewRouteConfigResourceTypeDecoder(c)},
+						version.V3ClusterURL:     {TypeURL: version.V3ClusterURL, TypeName: xdsresource.ClusterResourceTypeName, AllResourcesRequiredInSotW: true, Decoder: xdsresource.NewClusterResourceTypeDecoder(c, gServerCfgMap)},
+						version.V3EndpointsURL:   {TypeURL: version.V3EndpointsURL, TypeName: xdsresource.EndpointsResourceTypeName, AllResourcesRequiredInSotW: false, Decoder: xdsresource.NewEndpointsResourceTypeDecoder(c)},
+					},
+					MetricsReporter: &metricsReporter{recorder: stats.NewTestMetricsRecorder(), target: testTargetName},
+					TransportBuilder: grpctransport.NewBuilder(map[string]grpctransport.Config{
+						"insecure": {
+							Credentials: insecure.NewBundle(),
+							GRPCNewClient: func(target string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
+								opts = append(opts, serverCfg.DialOptions()...)
+								return grpc.NewClient(target, opts...)
+							}},
+					}),
+				}
+			},
+		},
+		{
+			name: "server features with trusted_xds_server",
+			bootstrapContents: []byte(fmt.Sprintf(`{
+				"xds_servers": [{"server_uri": "%s", "channel_creds": [{"type": "insecure"}], "server_features": ["trusted_xds_server"]}],
+				"node": {"id": "%s"}
+			}`, testXDSServerURL, testNodeID)),
+			wantXDSClientConfig: func(c *bootstrap.Config) xdsclient.Config {
+				node, serverCfg := c.Node(), c.XDSServers()[0]
+				serverFeature := xdsclient.ServerFeatureTrustedXDSServer
+				expectedServer := xdsclient.ServerConfig{ServerIdentifier: clients.ServerIdentifier{ServerURI: serverCfg.ServerURI(), Extensions: grpctransport.ServerIdentifierExtension{ConfigName: "insecure"}},
+					ServerFeature: serverFeature}
+				gServerCfgMap := map[xdsclient.ServerConfig]*bootstrap.ServerConfig{expectedServer: serverCfg}
+				return xdsclient.Config{
+					Servers:     []xdsclient.ServerConfig{expectedServer},
+					Node:        clients.Node{ID: node.GetId(), Cluster: node.GetCluster(), Metadata: node.Metadata, UserAgentName: node.UserAgentName, UserAgentVersion: node.GetUserAgentVersion()},
+					Authorities: map[string]xdsclient.Authority{},
+					ResourceTypes: map[string]xdsclient.ResourceType{
+						version.V3ListenerURL:    {TypeURL: version.V3ListenerURL, TypeName: xdsresource.ListenerResourceTypeName, AllResourcesRequiredInSotW: true, Decoder: xdsresource.NewListenerResourceTypeDecoder(c)},
+						version.V3RouteConfigURL: {TypeURL: version.V3RouteConfigURL, TypeName: xdsresource.RouteConfigTypeName, AllResourcesRequiredInSotW: false, Decoder: xdsresource.NewRouteConfigResourceTypeDecoder(c)},
+						version.V3ClusterURL:     {TypeURL: version.V3ClusterURL, TypeName: xdsresource.ClusterResourceTypeName, AllResourcesRequiredInSotW: true, Decoder: xdsresource.NewClusterResourceTypeDecoder(c, gServerCfgMap)},
+						version.V3EndpointsURL:   {TypeURL: version.V3EndpointsURL, TypeName: xdsresource.EndpointsResourceTypeName, AllResourcesRequiredInSotW: false, Decoder: xdsresource.NewEndpointsResourceTypeDecoder(c)},
+					},
+					MetricsReporter: &metricsReporter{recorder: stats.NewTestMetricsRecorder(), target: testTargetName},
+					TransportBuilder: grpctransport.NewBuilder(map[string]grpctransport.Config{
+						"insecure": {
+							Credentials: insecure.NewBundle(),
+							GRPCNewClient: func(target string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
+								opts = append(opts, serverCfg.DialOptions()...)
+								return grpc.NewClient(target, opts...)
+							}},
+					}),
+				}
+			},
+		},
+		{
+			name: "server features with ignore_resource_deletion and trusted_xds_server",
+			bootstrapContents: []byte(fmt.Sprintf(`{
+				"xds_servers": [{"server_uri": "%s", "channel_creds": [{"type": "insecure"}], "server_features": ["ignore_resource_deletion", "trusted_xds_server"]}],
+				"node": {"id": "%s"}
+			}`, testXDSServerURL, testNodeID)),
+			wantXDSClientConfig: func(c *bootstrap.Config) xdsclient.Config {
+				node, serverCfg := c.Node(), c.XDSServers()[0]
+				serverFeature := xdsclient.ServerFeatureTrustedXDSServer | xdsclient.ServerFeatureIgnoreResourceDeletion
+				expectedServer := xdsclient.ServerConfig{ServerIdentifier: clients.ServerIdentifier{ServerURI: serverCfg.ServerURI(), Extensions: grpctransport.ServerIdentifierExtension{ConfigName: "insecure"}},
+					ServerFeature: serverFeature}
 				gServerCfgMap := map[xdsclient.ServerConfig]*bootstrap.ServerConfig{expectedServer: serverCfg}
 				return xdsclient.Config{
 					Servers:     []xdsclient.ServerConfig{expectedServer},
diff --git a/internal/xds/xdsclient/xdsresource/unmarshal_cds_test.go b/internal/xds/xdsclient/xdsresource/unmarshal_cds_test.go
index 0fb70d63..cef43b25 100644
--- a/internal/xds/xdsclient/xdsresource/unmarshal_cds_test.go
+++ b/internal/xds/xdsclient/xdsresource/unmarshal_cds_test.go
@@ -1216,11 +1216,11 @@ func (s) TestValidateClusterWithSecurityConfig(t *testing.T) {
 					IdentityInstanceName: identityPluginInstance,
 					IdentityCertName:     identityCertName,
 					SubjectAltNameMatchers: []matcher.StringMatcher{
-						matcher.StringMatcherForTesting(newStringP(sanExact), nil, nil, nil, nil, true),
-						matcher.StringMatcherForTesting(nil, newStringP(sanPrefix), nil, nil, nil, false),
-						matcher.StringMatcherForTesting(nil, nil, newStringP(sanSuffix), nil, nil, false),
-						matcher.StringMatcherForTesting(nil, nil, nil, nil, sanRE, false),
-						matcher.StringMatcherForTesting(nil, nil, nil, newStringP(sanContains), nil, false),
+						matcher.NewExactStringMatcher(sanExact, true),
+						matcher.NewPrefixStringMatcher(sanPrefix, false),
+						matcher.NewSuffixStringMatcher(sanSuffix, false),
+						matcher.NewRegexStringMatcher(sanRE),
+						matcher.NewContainsStringMatcher(sanContains, false),
 					},
 				},
 				TelemetryLabels: xdsinternal.UnknownCSMLabels,
@@ -1283,11 +1283,11 @@ func (s) TestValidateClusterWithSecurityConfig(t *testing.T) {
 					IdentityInstanceName: identityPluginInstance,
 					IdentityCertName:     identityCertName,
 					SubjectAltNameMatchers: []matcher.StringMatcher{
-						matcher.StringMatcherForTesting(newStringP(sanExact), nil, nil, nil, nil, true),
-						matcher.StringMatcherForTesting(nil, newStringP(sanPrefix), nil, nil, nil, false),
-						matcher.StringMatcherForTesting(nil, nil, newStringP(sanSuffix), nil, nil, false),
-						matcher.StringMatcherForTesting(nil, nil, nil, nil, sanRE, false),
-						matcher.StringMatcherForTesting(nil, nil, nil, newStringP(sanContains), nil, false),
+						matcher.NewExactStringMatcher(sanExact, true),
+						matcher.NewPrefixStringMatcher(sanPrefix, false),
+						matcher.NewSuffixStringMatcher(sanSuffix, false),
+						matcher.NewRegexStringMatcher(sanRE),
+						matcher.NewContainsStringMatcher(sanContains, false),
 					},
 				},
 				TelemetryLabels: xdsinternal.UnknownCSMLabels,
@@ -1347,11 +1347,11 @@ func (s) TestValidateClusterWithSecurityConfig(t *testing.T) {
 					IdentityInstanceName: identityPluginInstance,
 					IdentityCertName:     identityCertName,
 					SubjectAltNameMatchers: []matcher.StringMatcher{
-						matcher.StringMatcherForTesting(newStringP(sanExact), nil, nil, nil, nil, true),
-						matcher.StringMatcherForTesting(nil, newStringP(sanPrefix), nil, nil, nil, false),
-						matcher.StringMatcherForTesting(nil, nil, newStringP(sanSuffix), nil, nil, false),
-						matcher.StringMatcherForTesting(nil, nil, nil, nil, sanRE, false),
-						matcher.StringMatcherForTesting(nil, nil, nil, newStringP(sanContains), nil, false),
+						matcher.NewExactStringMatcher(sanExact, true),
+						matcher.NewPrefixStringMatcher(sanPrefix, false),
+						matcher.NewSuffixStringMatcher(sanSuffix, false),
+						matcher.NewRegexStringMatcher(sanRE),
+						matcher.NewContainsStringMatcher(sanContains, false),
 					},
 				},
 				TelemetryLabels: xdsinternal.UnknownCSMLabels,
diff --git a/interop/grpc_testing/benchmark_service_grpc.pb.go b/interop/grpc_testing/benchmark_service_grpc.pb.go
index f6424a57..2b1b78cf 100644
--- a/interop/grpc_testing/benchmark_service_grpc.pb.go
+++ b/interop/grpc_testing/benchmark_service_grpc.pb.go
@@ -17,7 +17,7 @@
 
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
-// - protoc-gen-go-grpc v1.5.1
+// - protoc-gen-go-grpc v1.6.0
 // - protoc             v5.27.1
 // source: grpc/testing/benchmark_service.proto
 
diff --git a/interop/grpc_testing/report_qps_scenario_service_grpc.pb.go b/interop/grpc_testing/report_qps_scenario_service_grpc.pb.go
index 864762a3..7f10b0ec 100644
--- a/interop/grpc_testing/report_qps_scenario_service_grpc.pb.go
+++ b/interop/grpc_testing/report_qps_scenario_service_grpc.pb.go
@@ -17,7 +17,7 @@
 
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
-// - protoc-gen-go-grpc v1.5.1
+// - protoc-gen-go-grpc v1.6.0
 // - protoc             v5.27.1
 // source: grpc/testing/report_qps_scenario_service.proto
 
diff --git a/interop/grpc_testing/test_grpc.pb.go b/interop/grpc_testing/test_grpc.pb.go
index 32d68343..6dc85dd2 100644
--- a/interop/grpc_testing/test_grpc.pb.go
+++ b/interop/grpc_testing/test_grpc.pb.go
@@ -17,7 +17,7 @@
 
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
-// - protoc-gen-go-grpc v1.5.1
+// - protoc-gen-go-grpc v1.6.0
 // - protoc             v5.27.1
 // source: grpc/testing/test.proto
 
diff --git a/interop/grpc_testing/worker_service_grpc.pb.go b/interop/grpc_testing/worker_service_grpc.pb.go
index dca4069d..32a82163 100644
--- a/interop/grpc_testing/worker_service_grpc.pb.go
+++ b/interop/grpc_testing/worker_service_grpc.pb.go
@@ -17,7 +17,7 @@
 
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
-// - protoc-gen-go-grpc v1.5.1
+// - protoc-gen-go-grpc v1.6.0
 // - protoc             v5.27.1
 // source: grpc/testing/worker_service.proto
 
diff --git a/interop/stress/grpc_testing/metrics_grpc.pb.go b/interop/stress/grpc_testing/metrics_grpc.pb.go
index ec911670..3649de13 100644
--- a/interop/stress/grpc_testing/metrics_grpc.pb.go
+++ b/interop/stress/grpc_testing/metrics_grpc.pb.go
@@ -21,7 +21,7 @@
 
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
-// - protoc-gen-go-grpc v1.5.1
+// - protoc-gen-go-grpc v1.6.0
 // - protoc             v5.27.1
 // source: interop/stress/grpc_testing/metrics.proto
 
diff --git a/reflection/grpc_testing/test_grpc.pb.go b/reflection/grpc_testing/test_grpc.pb.go
index 44846e41..70703fb3 100644
--- a/reflection/grpc_testing/test_grpc.pb.go
+++ b/reflection/grpc_testing/test_grpc.pb.go
@@ -14,7 +14,7 @@
 
 // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
 // versions:
-// - protoc-gen-go-grpc v1.5.1
+// - protoc-gen-go-grpc v1.6.0
 // - protoc             v5.27.1
 // source: reflection/grpc_testing/test.proto