Repository: kubernetes-sigs/gateway-api

Issue:
v.1.4.0: new conformance test BackendTLSPolicyObservedGenerationBump makes invalid assertion (IMO)
The BackendTLSPolicyObservedGenerationBump has

```yaml
  validation:
    caCertificateRefs:
      - group: ""
        kind: ConfigMap
        # This ConfigMap is generated dynamically by the test suite.
        name: "backend-tls-checks-certificate"
    hostname: "abc.example.com"
```

`backend-tls-checks-certificate` does not exist, nor is it created by the test suite (despite the comment).

Our implementation sets Accepted=false for this, because there were no valid caCertificateRefs, therefor we cannot accept the policy. This is in line with the specification:
```
	// If ALL CACertificateRefs are invalid, the implementation MUST also
	// ensure the `Accepted` Condition on the BackendTLSPolicy is set to
	// `status: False`, with a Reason `NoValidCACertificate`.
```

Hints from issue comments:
@howardjohn: /assign

Solution patch:
diff --git a/apis/v1/gatewayclass_types.go b/apis/v1/gatewayclass_types.go
index f9b779c7..972d3504 100644
--- a/apis/v1/gatewayclass_types.go
+++ b/apis/v1/gatewayclass_types.go
@@ -304,7 +304,6 @@ type GatewayClassStatus struct {
 	// +optional
 	// +listType=map
 	// +listMapKey=name
-	// <gateway:experimental>
 	// +kubebuilder:validation:MaxItems=64
 	SupportedFeatures []SupportedFeature `json:"supportedFeatures,omitempty"`
 }
diff --git a/config/crd/standard/gateway.networking.k8s.io_gatewayclasses.yaml b/config/crd/standard/gateway.networking.k8s.io_gatewayclasses.yaml
index 2372cb88..68060bf3 100644
--- a/config/crd/standard/gateway.networking.k8s.io_gatewayclasses.yaml
+++ b/config/crd/standard/gateway.networking.k8s.io_gatewayclasses.yaml
@@ -236,6 +236,25 @@ spec:
                 x-kubernetes-list-map-keys:
                 - type
                 x-kubernetes-list-type: map
+              supportedFeatures:
+                description: |-
+                  SupportedFeatures is the set of features the GatewayClass support.
+                  It MUST be sorted in ascending alphabetical order by the Name key.
+                items:
+                  properties:
+                    name:
+                      description: |-
+                        FeatureName is used to describe distinct features that are covered by
+                        conformance tests.
+                      type: string
+                  required:
+                  - name
+                  type: object
+                maxItems: 64
+                type: array
+                x-kubernetes-list-map-keys:
+                - name
+                x-kubernetes-list-type: map
             type: object
         required:
         - spec
@@ -461,6 +480,25 @@ spec:
                 x-kubernetes-list-map-keys:
                 - type
                 x-kubernetes-list-type: map
+              supportedFeatures:
+                description: |-
+                  SupportedFeatures is the set of features the GatewayClass support.
+                  It MUST be sorted in ascending alphabetical order by the Name key.
+                items:
+                  properties:
+                    name:
+                      description: |-
+                        FeatureName is used to describe distinct features that are covered by
+                        conformance tests.
+                      type: string
+                  required:
+                  - name
+                  type: object
+                maxItems: 64
+                type: array
+                x-kubernetes-list-map-keys:
+                - name
+                x-kubernetes-list-type: map
             type: object
         required:
         - spec
diff --git a/conformance/conformance.go b/conformance/conformance.go
index 39213105..642a6eb2 100644
--- a/conformance/conformance.go
+++ b/conformance/conformance.go
@@ -93,6 +93,7 @@ func DefaultOptions(t *testing.T) suite.ConformanceOptions {
 		ExemptFeatures:             exemptFeatures,
 		ManifestFS:                 []fs.FS{&Manifests},
 		GatewayClassName:           *flags.GatewayClassName,
+		MeshName:                   *flags.MeshName,
 		Implementation:             implementation,
 		Mode:                       *flags.Mode,
 		NamespaceAnnotations:       namespaceAnnotations,
diff --git a/conformance/utils/flags/flags.go b/conformance/utils/flags/flags.go
index 95da9eaa..1465df68 100644
--- a/conformance/utils/flags/flags.go
+++ b/conformance/utils/flags/flags.go
@@ -30,6 +30,7 @@ const (
 
 var (
 	GatewayClassName           = flag.String("gateway-class", "gateway-conformance", "Name of GatewayClass to use for tests")
+	MeshName                   = flag.String("mesh-name", "", "Name of Mesh to use for tests")
 	ShowDebug                  = flag.Bool("debug", false, "Whether to print debug logs")
 	CleanupBaseResources       = flag.Bool("cleanup-base-resources", true, "Whether to cleanup base test resources after the run")
 	SupportedFeatures          = flag.String("supported-features", "", "Supported features included in conformance tests suites")
diff --git a/conformance/utils/suite/conformance.go b/conformance/utils/suite/conformance.go
index 436b3ed2..12db14d6 100644
--- a/conformance/utils/suite/conformance.go
+++ b/conformance/utils/suite/conformance.go
@@ -68,7 +68,7 @@ func (test *ConformanceTest) Run(t *testing.T, suite *ConformanceTestSuite) {
 
 	for _, manifestLocation := range test.Manifests {
 		tlog.Logf(t, "Applying %s", manifestLocation)
-		suite.Applier.MustApplyWithCleanup(t, suite.Client, suite.TimeoutConfig, manifestLocation, suite.Cleanup)
+		suite.Applier.MustApplyWithCleanup(t, suite.Client, suite.TimeoutConfig, manifestLocation, true)
 	}
 
 	if featuresInfo != "" {
diff --git a/conformance/utils/suite/suite.go b/conformance/utils/suite/suite.go
index 31db06c5..8dcd009e 100644
--- a/conformance/utils/suite/suite.go
+++ b/conformance/utils/suite/suite.go
@@ -39,6 +39,7 @@ import (
 
 	gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
 	"sigs.k8s.io/gateway-api/apis/v1beta1"
+	xmeshv1alpha1 "sigs.k8s.io/gateway-api/apisx/v1alpha1"
 	confv1 "sigs.k8s.io/gateway-api/conformance/apis/v1"
 	"sigs.k8s.io/gateway-api/conformance/utils/config"
 	"sigs.k8s.io/gateway-api/conformance/utils/flags"
@@ -65,6 +66,7 @@ type ConformanceTestSuite struct {
 	RoundTripper             roundtripper.RoundTripper
 	GRPCClient               grpc.Client
 	GatewayClassName         string
+	MeshName                 string
 	ControllerName           string
 	Debug                    bool
 	Cleanup                  bool
@@ -136,6 +138,7 @@ type ConformanceOptions struct {
 	Clientset            clientset.Interface
 	RestConfig           *rest.Config
 	GatewayClassName     string
+	MeshName             string
 	AddressType          string
 	Debug                bool
 	RoundTripper         roundtripper.RoundTripper
@@ -206,20 +209,23 @@ func NewConformanceTestSuite(options ConformanceOptions) (*ConformanceTestSuite,
 		supportedFeatures = features.SetsToNamesSet(features.AllFeatures)
 	} else if shouldInferSupportedFeatures(&options) {
 		var err error
-		supportedFeatures, err = fetchSupportedFeatures(options.Client, options.GatewayClassName)
-		if err != nil {
-			return nil, fmt.Errorf("cannot infer supported features: %w", err)
+		if options.GatewayClassName != "" {
+			supportedFeatures, err = fetchGatewayClassSupportedFeatures(options.Client, options.GatewayClassName)
+			if err != nil {
+				return nil, fmt.Errorf("cannot infer supported features from GWC: %w", err)
+			}
 		}
 
-		// If Mesh features are populated in the GatewayClass we remove them from the supported features set.
-		meshFeatureNames := features.SetsToNamesSet(features.MeshCoreFeatures, features.MeshExtendedFeatures)
-		for _, f := range supportedFeatures.UnsortedList() {
-			if meshFeatureNames.Has(f) {
-				supportedFeatures.Delete(f)
-				fmt.Printf("WARNING: Mesh feature %q should not be populated in GatewayClass, skipping...", f)
+		supportedMeshFeatures := FeaturesSet{}
+		if options.MeshName != "" {
+			supportedMeshFeatures, err = fetchMeshSupportedFeatures(options.Client, options.MeshName)
+			if err != nil {
+				return nil, fmt.Errorf("cannot infer supported features from XMesh: %w", err)
 			}
 		}
+
 		source = supportedFeaturesSourceInferred
+		supportedFeatures = supportedFeatures.Union(supportedMeshFeatures)
 	}
 
 	// If features were not inferred from Status, it's a GWC issue.
@@ -606,24 +612,59 @@ func ParseConformanceProfiles(p string) sets.Set[ConformanceProfileName] {
 	return res
 }
 
-func fetchSupportedFeatures(client client.Client, gatewayClassName string) (FeaturesSet, error) {
+func fetchGatewayClassSupportedFeatures(client client.Client, gatewayClassName string) (FeaturesSet, error) {
 	if gatewayClassName == "" {
 		return nil, fmt.Errorf("GatewayClass name must be provided to fetch supported features")
 	}
 	gwc := &gatewayv1.GatewayClass{}
 	err := client.Get(context.TODO(), types.NamespacedName{Name: gatewayClassName}, gwc)
 	if err != nil {
-		return nil, fmt.Errorf("fetchSupportedFeatures(): %w", err)
+		return nil, fmt.Errorf("fetchGatewayClassSupportedFeatures(): %w", err)
 	}
 
 	fs := FeaturesSet{}
 	for _, feature := range gwc.Status.SupportedFeatures {
 		fs.Insert(features.FeatureName(feature.Name))
 	}
+
+	// If Mesh features are populated in the GatewayClass we remove them from the supported features set.
+	meshFeatureNames := features.SetsToNamesSet(features.MeshCoreFeatures, features.MeshExtendedFeatures)
+	for _, f := range fs.UnsortedList() {
+		if meshFeatureNames.Has(f) {
+			fs.Delete(f)
+			fmt.Printf("WARNING: Mesh feature %q should not be populated in GatewayClass, skipping...", f)
+		}
+	}
 	fmt.Printf("Supported features for GatewayClass %s: %v\n", gatewayClassName, fs.UnsortedList())
 	return fs, nil
 }
 
+func fetchMeshSupportedFeatures(client client.Client, meshName string) (FeaturesSet, error) {
+	if meshName == "" {
+		return nil, fmt.Errorf("mesh name must be provided to fetch supported features")
+	}
+	xmesh := &xmeshv1alpha1.XMesh{}
+	err := client.Get(context.TODO(), types.NamespacedName{Name: meshName}, xmesh)
+	if err != nil {
+		return nil, fmt.Errorf("fetchMeshSupportedFeatures(): %w", err)
+	}
+
+	fs := FeaturesSet{}
+	for _, feature := range xmesh.Status.SupportedFeatures {
+		fs.Insert(features.FeatureName(feature.Name))
+	}
+
+	gwcFeatureNames := features.SetsToNamesSet(features.GatewayCoreFeatures, features.GatewayExtendedFeatures)
+	for _, f := range fs.UnsortedList() {
+		if gwcFeatureNames.Has(f) {
+			fs.Delete(f)
+			fmt.Printf("WARNING: Mesh feature %q should not be populated in XMesh.Status, skipping...", f)
+		}
+	}
+	fmt.Printf("Supported features for XMesh%s: %v\n", meshName, fs.UnsortedList())
+	return fs, nil
+}
+
 // shouldInferSupportedFeatures checks if any flags were supplied for manually
 // picking what to test. Inferred supported features are only used when no flags
 // are set.
diff --git a/geps/gep-1897/index.md b/geps/gep-1897/index.md
index 149d6c64..2b2159f7 100644
--- a/geps/gep-1897/index.md
+++ b/geps/gep-1897/index.md
@@ -1,7 +1,7 @@
 # GEP-1897: BackendTLSPolicy - Explicit Backend TLS Connection Configuration
 
 * Issue: [#1897](https://github.com/kubernetes-sigs/gateway-api/issues/1897)
-* Status: Experimental
+* Status: Standard
 
 ## TLDR
 
@@ -30,11 +30,9 @@ Gateway to backend**, such as server name indication and trusted CA certificates
 ## Longer Term Goals
 
 These are worthy goals, but deserve a different GEP for proper attention.  This GEP is concerned entirely with the
-the hop between gateway client and backend.
-
-1. [TCPRoute](../../reference/spec.md#gateway.networking.k8s.io/v1alpha2.TCPRoute) and
-[GRPCRoute](../../reference/spec.md#gateway.networking.k8s.io/v1alpha2.GRPCRoute) use cases
-are not addressed here, because at this point in time these two route types are not graduated to beta.
+hop between gateway client and backend.
+1. [TCPRoute](../../apis/v1alpha2/tcproute_types.go) use cases are not addressed here, because at this point in time
+TCPRoute is not graduated to beta.
 2. Mutual TLS (mTLS) use cases are intentionally out of scope for this GEP for two reasons.  First, the design of Gateway
 API is backend-attached and does not currently support mutual authentication, and also because this GEP does not
 address the case where connections to TLS are **implicitly configured** on behalf of the user, which is the norm for mTLS.
@@ -52,18 +50,21 @@ These are worthy goals, but will not be covered by this GEP.
 2. Providing a mechanism to decorate multiple route instances
 3. TLSRoute use cases
 4. UDPRoute use cases
-5. Controlling TLS versions or cipher suites used in TLS handshakes. (Use case #5 in [Gateway API TLS Use Cases](#references))
-6. Controlling certificates used by more than one workload (#6 in [Gateway API TLS Use Cases](#references))
-7. Client certificate settings used in TLS **from external clients to the
+5. GRPCRoute use cases
+6. Controlling TLS versions or cipher suites used in TLS handshakes. (Use case #5 in [Gateway API TLS Use Cases](#references))
+7. Controlling certificates used by more than one workload (#6 in [Gateway API TLS Use Cases](#references))
+8. Client certificate settings used in TLS **from external clients to the
 Listener** (#7 in [Gateway API TLS Use Cases](#references))
-8. Service Mesh "mesh transport security".
-9. Providing a mechanism for the cluster operator to override gateway to backend TLS settings.
+9. Service Mesh "mesh transport security".
+10. Providing a mechanism for the cluster operator to override gateway to backend TLS settings.
 
-> It is very common for service mesh implementations to implement some form of transparent transport security, whether that is WireGuard, mTLS, or others.
-> This is completely orthogonal to the use cases being tackled by this GEP.
-> * The "mesh transport security" is something invisible to the user's application, and is simply used to secure communication between components in the mesh.
+> It is very common for service mesh implementations to implement some form of transparent transport security, whether
+> that is WireGuard, mTLS, or others. This is completely orthogonal to the use cases being tackled by this GEP.
+> * The "mesh transport security" is something invisible to the user's application, and is simply used to secure
+> communication between components in the mesh.
 > * This proposal, instead, explicitly calls for sending TLS **to the user's application**.
-> However, this does not mean service meshes are outside of scope for this proposal, merely that only the application-level TLS configuration is in scope.
+> However, this does not mean service meshes are outside of scope for this proposal, merely that only the
+> application-level TLS configuration is in scope.
 
 ![](images/mesh.png "Mesh transport")
 
@@ -84,7 +85,6 @@ backend pod).  As mentioned, this solution satisfies the use case in which the b
 has its own certificate and the gateway client needs to know how to connect to the backend pod.
 
 ![image depicting TLS termination types](images/1897-TLStermtypes.png "TLS termination types")
-
 Gateway API is missing a mechanism for separately providing the details for the backend TLS handshake,
 including (but not limited to):
 
@@ -157,8 +157,8 @@ substituted without blocking acceptance of the content of the API change.
 
 The selection of the applicable Gateway API persona is important in the design of BackendTLSPolicy, because it provides
 a way to explicitly describe the _expectations_ of the connection to the application.
-In this GEP, BackendTLSPolicy will be configured only by the application developer Gateway API persona to tell gateway clients how to connect to
-the application, from a TLS perspective.
+In this GEP, BackendTLSPolicy will be configured only by the application developer Gateway API persona to tell gateway
+clients how to connect to the application, from a TLS perspective.
 Future iterations *may* expand this to additionally allow consumer overrides; see [Future plans](#future-plans).
 
 During the course of discussion of this proposal, we did consider allowing the cluster operator persona to have some access
@@ -181,7 +181,8 @@ as a TLS Client:
 BackendTLSPolicy is defined as a Direct Policy Attachment without defaults or overrides, applied to a Service that
 accesses the backend in question, where the BackendTLSPolicy resides in the same namespace as the Service it is
 applied to.  For now, the BackendTLSPolicy and the Service must reside in the same namespace in order to prevent the
-complications involved with sharing trust across namespace boundaries (see [Future plans](#future-plans)).  We chose the Service resource as a target,
+complications involved with sharing trust across namespace boundaries (see [Future plans](#future-plans)).  We chose the Service
+resource as a target,
 rather than the Route resource, so that we can reuse the same BackendTLSPolicy for all the different Routes that
 might point to this Service.
 
@@ -196,7 +197,10 @@ that is appropriate, such as one of the HTTP error codes: 400 (Bad Request), 401
 other signal that makes the failure sufficiently clear to the requester without revealing too much about the transaction,
 based on established security requirements.
 
-BackendTLSPolicy applies only to TCP traffic. If a policy explicitly attaches to a UDP port of a Service (that is, the `targetRef` has a `sectionName` specifying a single port or the service has only 1 port), the `Accepted: False` Condition with `Reason: Invalid` MUST be set. If the policy attaches to a mix of TCP and UDP ports, implementations SHOULD include a warning in the `Accepted` condition message (`ancestors.conditions`); the policy will only be effective for the TCP ports.
+BackendTLSPolicy applies only to TCP traffic. If a policy explicitly attaches to a UDP port of a Service (that is, the
+`targetRef` has a `sectionName` specifying a single port or the service has only 1 port), the `Accepted: False` Condition
+with `Reason: Invalid` MUST be set. If the policy attaches to a mix of TCP and UDP ports, implementations SHOULD include
+a warning in the `Accepted` condition message (`ancestors.conditions`); the policy will only be effective for the TCP ports.
 
 All policy resources must include `TargetRefs` with the fields specified
 in [PolicyTargetReference](https://github.com/kubernetes-sigs/gateway-api/blob/a33a934af9ec6997b34fd9b00d2ecd13d143e48b/apis/v1alpha2/policy_types.go#L24-L41).
@@ -214,12 +218,20 @@ configuration. CACertificateRefs is an implementation-specific slice of
 named object references, each containing a single cert. We originally proposed to follow the convention established by the
 [CertificateRefs field on Gateway](https://github.com/kubernetes-sigs/gateway-api/blob/18e79909f7310aafc625ba7c862dfcc67b385250/apis/v1beta1/gateway_types.go#L340)
 , but the CertificateRef requires both a tls.key and tls.crt and a certificate reference only requires the tls.crt.
-If any of the CACertificateRefs cannot be resolved (e.g., the referenced resource does not exist) or is misconfigured (e.g., ConfigMap does not contain a key named `ca.crt`), the `ResolvedRefs` status condition MUST be set to `False` with `Reason: InvalidCACertificateRef`. Connections using that CACertificateRef MUST fail, and the client MUST receive an HTTP 5xx error response.
-References to objects with an unsupported Group and Kind are not valid, and MUST be rejected by the implementation with the `ResolvedRefs` status condition set to `False` and `Reason: InvalidKind`.
-Implementations MAY perform further validation of the certificate content (i.e., checking expiry or enforcing specific formats). If they do, they MUST ensure that the `ResolvedRefs` Condition is `False` and use an implementation-specific `Reason`, like `ExpiredCertificate` or similar.
-If `ResolvedRefs` Condition is `False` implementations SHOULD include a message specifying which references are invalid and explaining why.
-
-If all CertificateRefs cannot be resolved, the BackendTLSPolicy is considered invalid and the implementation MUST set the `Accepted` Condition to `False`, with a reason of `NoValidCACertificate` and a message explaining this.
+If any of the CACertificateRefs cannot be resolved (e.g., the referenced resource does not exist) or is misconfigured
+(e.g., ConfigMap does not contain a key named `ca.crt`), the `ResolvedRefs` status condition MUST be set to `False` with
+`Reason: InvalidCACertificateRef`. Connections using that CACertificateRef MUST fail, and the client MUST receive an
+HTTP 5xx error response.
+References to objects with an unsupported Group and Kind are not valid, and MUST be rejected by the implementation with
+the `ResolvedRefs` status condition set to `False` and `Reason: InvalidKind`.
+Implementations MAY perform further validation of the certificate content (i.e., checking expiry or enforcing specific
+formats). If they do, they MUST ensure that the `ResolvedRefs` Condition is `False` and use an implementation-specific
+`Reason`, like `ExpiredCertificate` or similar.
+If `ResolvedRefs` Condition is `False` implementations SHOULD include a message specifying which references are invalid
+and explaining why.
+
+If all CertificateRefs cannot be resolved, the BackendTLSPolicy is considered invalid and the implementation MUST set
+the `Accepted` Condition to `False`, with a reason of `NoValidCACertificate` and a message explaining this.
 
 WellKnownCACertificates is an optional enum that allows users to specify whether to use the set of CA certificates trusted by the
 Gateway (WellKnownCACertificates specified as "System"), or to use the existing CACertificateRefs (WellKnownCACertificates
@@ -228,13 +240,17 @@ these certificates are obtained from the underlying operating system. CACertific
 references to Kubernetes objects that contain PEM-encoded TLS certificates, which are used to establish a TLS handshake
 between the gateway and backend pod. References to a resource in a different namespace are invalid.
 If ClientCertificateRefs is unspecified, then WellKnownCACertificates must be set to "System" for a valid configuration.
-If WellKnownCACertificates is unspecified, then CACertificateRefs must be specified with at least one entry for a valid configuration.
-If an implementation does not support the WellKnownCACertificates, or the provided value is unsupported,the BackendTLSPolicy is considered invalid, and the implementation MUST set the `Accepted` Condition to `False`, with a reason of `Invalid` and a message explaining this.
+If WellKnownCACertificates is unspecified, then CACertificateRefs must be specified with at least one entry for a valid
+configuration.
+If an implementation does not support the WellKnownCACertificates, or the provided value is unsupported,the
+BackendTLSPolicy is considered invalid, and the implementation MUST set the `Accepted` Condition to `False`, with a
+reason of `Invalid` and a message explaining this.
 
 For an invalid BackendTLSPolicy, implementations MUST NOT fall back to unencrypted (plaintext) connections. 
 Instead, the corresponding TLS connection MUST fail, and the client MUST receive an HTTP 5xx error response.
 
-Implementations MUST NOT modify any status other than their own. Ownership of a status is determined by the `controllerName`, which identifies the responsible controller.
+Implementations MUST NOT modify any status other than their own. Ownership of a status is determined by the `controllerName`,
+which identifies the responsible controller.
 
 The `Hostname` field is required and is to be used to configure the SNI the Gateway should use to connect to the backend.
 Implementations must validate that at least one name in the certificate served by the backend matches this field.
@@ -245,10 +261,309 @@ but may be added in the future.
 We originally proposed allowing the configuration of expected TLS versions, but determined that this was [not needed in
 the first round](https://github.com/kubernetes-sigs/gateway-api/pull/2113#issuecomment-1696127092).
 
-Thus, the following additions would be made to the Gateway API:
+Thus, the following additions would be made to Gateway API.  See the
+[BackendTLSPolicy API](https://kubernetes-sigs/gateway-api/blob/main/apis/v1/backendtlspolicy_types.go) for more
+details.
 
 ```go
-//TODO: Will update this section once API changes from PR 2955 are approved.
+// +genclient
+// +kubebuilder:object:root=true
+// +kubebuilder:subresource:status
+// +kubebuilder:storageversion
+// +kubebuilder:resource:categories=gateway-api,shortName=btlspolicy
+// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
+//
+// BackendTLSPolicy is a Direct Attached Policy.
+// +kubebuilder:metadata:labels="gateway.networking.k8s.io/policy=Direct"
+
+// BackendTLSPolicy provides a way to configure how a Gateway
+// connects to a Backend via TLS.
+type BackendTLSPolicy struct {
+	metav1.TypeMeta `json:",inline"`
+	// +optional
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+
+	// Spec defines the desired state of BackendTLSPolicy.
+	// +required
+	Spec BackendTLSPolicySpec `json:"spec"`
+
+	// Status defines the current state of BackendTLSPolicy.
+	// +optional
+	Status PolicyStatus `json:"status,omitempty"`
+}
+
+// BackendTLSPolicyList contains a list of BackendTLSPolicies
+// +kubebuilder:object:root=true
+type BackendTLSPolicyList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata,omitempty"`
+	Items           []BackendTLSPolicy `json:"items"`
+}
+
+// BackendTLSPolicySpec defines the desired state of BackendTLSPolicy.
+//
+// Support: Extended
+type BackendTLSPolicySpec struct {
+	// TargetRefs identifies an API object to apply the policy to.
+	// Only Services have Extended support. Implementations MAY support
+	// additional objects, with Implementation Specific support.
+	// Note that this config applies to the entire referenced resource
+	// by default, but this default may change in the future to provide
+	// a more granular application of the policy.
+	//
+	// TargetRefs must be _distinct_. This means either that:
+	//
+	// * They select different targets. If this is the case, then targetRef
+	//   entries are distinct. In terms of fields, this means that the
+	//   multi-part key defined by `group`, `kind`, and `name` must
+	//   be unique across all targetRef entries in the BackendTLSPolicy.
+	// * They select different sectionNames in the same target.
+	//
+	//
+	// When more than one BackendTLSPolicy selects the same target and
+	// sectionName, implementations MUST determine precedence using the
+	// following criteria, continuing on ties:
+	//
+	// * The older policy by creation timestamp takes precedence. For
+	//   example, a policy with a creation timestamp of "2021-07-15
+	//   01:02:03" MUST be given precedence over a policy with a
+	//   creation timestamp of "2021-07-15 01:02:04".
+	// * The policy appearing first in alphabetical order by {name}.
+	//   For example, a policy named `bar` is given precedence over a
+	//   policy named `baz`.
+	//
+	// For any BackendTLSPolicy that does not take precedence, the
+	// implementation MUST ensure the `Accepted` Condition is set to
+	// `status: False`, with Reason `Conflicted`.
+	//
+	// Support: Extended for Kubernetes Service
+	//
+	// Support: Implementation-specific for any other resource
+	//
+	// +required
+	// +listType=atomic
+	// +kubebuilder:validation:MinItems=1
+	// +kubebuilder:validation:MaxItems=16
+	// +kubebuilder:validation:XValidation:message="sectionName must be specified when targetRefs includes 2 or more references to the same target",rule="self.all(p1, self.all(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name ? ((!has(p1.sectionName) || p1.sectionName == '') == (!has(p2.sectionName) || p2.sectionName == '')) : true))"
+	// +kubebuilder:validation:XValidation:message="sectionName must be unique when targetRefs includes 2 or more references to the same target",rule="self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.sectionName) || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName == '')) || (has(p1.sectionName) && has(p2.sectionName) && p1.sectionName == p2.sectionName))))"
+	TargetRefs []LocalPolicyTargetReferenceWithSectionName `json:"targetRefs"`
+
+	// Validation contains backend TLS validation configuration.
+	// +required
+	Validation BackendTLSPolicyValidation `json:"validation"`
+
+	// Options are a list of key/value pairs to enable extended TLS
+	// configuration for each implementation. For example, configuring the
+	// minimum TLS version or supported cipher suites.
+	//
+	// A set of common keys MAY be defined by the API in the future. To avoid
+	// any ambiguity, implementation-specific definitions MUST use
+	// domain-prefixed names, such as `example.com/my-custom-option`.
+	// Un-prefixed names are reserved for key names defined by Gateway API.
+	//
+	// Support: Implementation-specific
+	//
+	// +optional
+	// +kubebuilder:validation:MaxProperties=16
+	Options map[AnnotationKey]AnnotationValue `json:"options,omitempty"`
+}
+
+// BackendTLSPolicyValidation contains backend TLS validation configuration.
+// +kubebuilder:validation:XValidation:message="must not contain both CACertificateRefs and WellKnownCACertificates",rule="!(has(self.caCertificateRefs) && size(self.caCertificateRefs) > 0 && has(self.wellKnownCACertificates) && self.wellKnownCACertificates != \"\")"
+// +kubebuilder:validation:XValidation:message="must specify either CACertificateRefs or WellKnownCACertificates",rule="(has(self.caCertificateRefs) && size(self.caCertificateRefs) > 0 || has(self.wellKnownCACertificates) && self.wellKnownCACertificates != \"\")"
+type BackendTLSPolicyValidation struct {
+	// CACertificateRefs contains one or more references to Kubernetes objects that
+	// contain a PEM-encoded TLS CA certificate bundle, which is used to
+	// validate a TLS handshake between the Gateway and backend Pod.
+	//
+	// If CACertificateRefs is empty or unspecified, then WellKnownCACertificates must be
+	// specified. Only one of CACertificateRefs or WellKnownCACertificates may be specified,
+	// not both. If CACertificateRefs is empty or unspecified, the configuration for
+	// WellKnownCACertificates MUST be honored instead if supported by the implementation.
+	//
+	// A CACertificateRef is invalid if:
+	//
+	// * It refers to a resource that cannot be resolved (e.g., the referenced resource
+	//   does not exist) or is misconfigured (e.g., a ConfigMap does not contain a key
+	//   named `ca.crt`). In this case, the Reason must be set to `InvalidCACertificateRef`
+	//   and the Message of the Condition must indicate which reference is invalid and why.
+	//
+	// * It refers to an unknown or unsupported kind of resource. In this case, the Reason
+	//   must be set to `InvalidKind` and the Message of the Condition must explain which
+	//   kind of resource is unknown or unsupported.
+	//
+	// * It refers to a resource in another namespace. This may change in future
+	//   spec updates.
+	//
+	// Implementations MAY choose to perform further validation of the certificate
+	// content (e.g., checking expiry or enforcing specific formats). In such cases,
+	// an implementation-specific Reason and Message must be set for the invalid reference.
+	//
+	// In all cases, the implementation MUST ensure the `ResolvedRefs` Condition on
+	// the BackendTLSPolicy is set to `status: False`, with a Reason and Message
+	// that indicate the cause of the error. Connections using an invalid
+	// CACertificateRef MUST fail, and the client MUST receive an HTTP 5xx error
+	// response. If ALL CACertificateRefs are invalid, the implementation MUST also
+	// ensure the `Accepted` Condition on the BackendTLSPolicy is set to
+	// `status: False`, with a Reason `NoValidCACertificate`.
+	//
+	//
+	// A single CACertificateRef to a Kubernetes ConfigMap kind has "Core" support.
+	// Implementations MAY choose to support attaching multiple certificates to
+	// a backend, but this behavior is implementation-specific.
+	//
+	// Support: Core - An optional single reference to a Kubernetes ConfigMap,
+	// with the CA certificate in a key named `ca.crt`.
+	//
+	// Support: Implementation-specific - More than one reference, other kinds
+	// of resources, or a single reference that includes multiple certificates.
+	//
+	// +optional
+	// +listType=atomic
+	// +kubebuilder:validation:MaxItems=8
+	CACertificateRefs []LocalObjectReference `json:"caCertificateRefs,omitempty"`
+
+	// WellKnownCACertificates specifies whether system CA certificates may be used in
+	// the TLS handshake between the gateway and backend pod.
+	//
+	// If WellKnownCACertificates is unspecified or empty (""), then CACertificateRefs
+	// must be specified with at least one entry for a valid configuration. Only one of
+	// CACertificateRefs or WellKnownCACertificates may be specified, not both.
+	// If an implementation does not support the WellKnownCACertificates field, or
+	// the supplied value is not recognized, the implementation MUST ensure the
+	// `Accepted` Condition on the BackendTLSPolicy is set to `status: False`, with
+	// a Reason `Invalid`.
+	//
+	// Support: Implementation-specific
+	//
+	// +optional
+	// +listType=atomic
+	WellKnownCACertificates *WellKnownCACertificatesType `json:"wellKnownCACertificates,omitempty"`
+
+	// Hostname is used for two purposes in the connection between Gateways and
+	// backends:
+	//
+	// 1. Hostname MUST be used as the SNI to connect to the backend (RFC 6066).
+	// 2. Hostname MUST be used for authentication and MUST match the certificate
+	//    served by the matching backend, unless SubjectAltNames is specified.
+	// 3. If SubjectAltNames are specified, Hostname can be used for certificate selection
+	//    but MUST NOT be used for authentication. If you want to use the value
+	//    of the Hostname field for authentication, you MUST add it to the SubjectAltNames list.
+	//
+	// Support: Core
+	//
+	// +required
+	Hostname PreciseHostname `json:"hostname"`
+
+	// SubjectAltNames contains one or more Subject Alternative Names.
+	// When specified the certificate served from the backend MUST
+	// have at least one Subject Alternate Name matching one of the specified SubjectAltNames.
+	//
+	// Support: Extended
+	//
+	// +optional
+	// +listType=atomic
+	// +kubebuilder:validation:MaxItems=5
+	SubjectAltNames []SubjectAltName `json:"subjectAltNames,omitempty"`
+}
+
+// SubjectAltName represents Subject Alternative Name.
+// +kubebuilder:validation:XValidation:message="SubjectAltName element must contain Hostname, if Type is set to Hostname",rule="!(self.type == \"Hostname\" && (!has(self.hostname) || self.hostname == \"\"))"
+// +kubebuilder:validation:XValidation:message="SubjectAltName element must not contain Hostname, if Type is not set to Hostname",rule="!(self.type != \"Hostname\" && has(self.hostname) && self.hostname != \"\")"
+// +kubebuilder:validation:XValidation:message="SubjectAltName element must contain URI, if Type is set to URI",rule="!(self.type == \"URI\" && (!has(self.uri) || self.uri == \"\"))"
+// +kubebuilder:validation:XValidation:message="SubjectAltName element must not contain URI, if Type is not set to URI",rule="!(self.type != \"URI\" && has(self.uri) && self.uri != \"\")"
+type SubjectAltName struct {
+	// Type determines the format of the Subject Alternative Name. Always required.
+	//
+	// Support: Core
+	//
+	// +required
+	Type SubjectAltNameType `json:"type"`
+
+	// Hostname contains Subject Alternative Name specified in DNS name format.
+	// Required when Type is set to Hostname, ignored otherwise.
+	//
+	// Support: Core
+	//
+	// +optional
+	Hostname Hostname `json:"hostname,omitempty"`
+
+	// URI contains Subject Alternative Name specified in a full URI format.
+	// It MUST include both a scheme (e.g., "http" or "ftp") and a scheme-specific-part.
+	// Common values include SPIFFE IDs like "spiffe://mycluster.example.com/ns/myns/sa/svc1sa".
+	// Required when Type is set to URI, ignored otherwise.
+	//
+	// Support: Core
+	//
+	// +optional
+	URI AbsoluteURI `json:"uri,omitempty"`
+}
+
+// WellKnownCACertificatesType is the type of CA certificate that will be used
+// when the caCertificateRefs field is unspecified.
+// +kubebuilder:validation:Enum=System
+type WellKnownCACertificatesType string
+
+const (
+	// WellKnownCACertificatesSystem indicates that well known system CA certificates should be used.
+	WellKnownCACertificatesSystem WellKnownCACertificatesType = "System"
+)
+
+// SubjectAltNameType is the type of the Subject Alternative Name.
+// +kubebuilder:validation:Enum=Hostname;URI
+type SubjectAltNameType string
+
+const (
+	// HostnameSubjectAltNameType specifies hostname-based SAN.
+	//
+	// Support: Core
+	HostnameSubjectAltNameType SubjectAltNameType = "Hostname"
+
+	// URISubjectAltNameType specifies URI-based SAN, e.g. SPIFFE id.
+	//
+	// Support: Core
+	URISubjectAltNameType SubjectAltNameType = "URI"
+)
+
+const (
+	// This reason is used with the "Accepted" condition when it is
+	// set to false because all CACertificateRefs of the
+	// BackendTLSPolicy are invalid.
+	BackendTLSPolicyReasonNoValidCACertificate PolicyConditionReason = "NoValidCACertificate"
+)
+
+const (
+	// This condition indicates whether the controller was able to resolve all
+	// object references for the BackendTLSPolicy.
+	//
+	// Possible reasons for this condition to be True are:
+	//
+	// * "ResolvedRefs"
+	//
+	// Possible reasons for this condition to be False are:
+	//
+	// * "InvalidCACertificateRef"
+	// * "InvalidKind"
+	//
+	// Controllers may raise this condition with other reasons, but should
+	// prefer to use the reasons listed above to improve interoperability.
+	BackendTLSPolicyConditionResolvedRefs PolicyConditionType = "ResolvedRefs"
+
+	// This reason is used with the "ResolvedRefs" condition when the condition
+	// is true.
+	BackendTLSPolicyReasonResolvedRefs PolicyConditionReason = "ResolvedRefs"
+
+	// This reason is used with the "ResolvedRefs" condition when one of the
+	// BackendTLSPolicy's CACertificateRefs is invalid.
+	// A CACertificateRef is considered invalid when it refers to a nonexistent
+	// resource or when the data within that resource is malformed.
+	BackendTLSPolicyReasonInvalidCACertificateRef PolicyConditionReason = "InvalidCACertificateRef"
+
+	// This reason is used with the "ResolvedRefs" condition when one of the
+	// BackendTLSPolicy's CACertificateRefs references an unknown or unsupported
+	// Group and/or Kind.
+	BackendTLSPolicyReasonInvalidKind PolicyConditionReason = "InvalidKind"
+)
 ```
 
 ## How a client behaves
@@ -262,12 +577,16 @@ For instance, the following will all have the gateway client add TLS if the back
 * A Gateway accepts and terminates TLS on an HTTPS listener
 * A Gateway accepts traffic on a TCP listener
 
-There is no need for a Gateway that accepts traffic with `Mode: Passthrough` to do anything differently here, but implementations MAY choose to treat TLS passthrough as a special case. Implementations that do this SHOULD clearly document their approach if BackendTLSPolicy is treated differently for TLS passthrough.
+There is no need for a Gateway that accepts traffic with `Mode: Passthrough` to do anything differently here, but
+implementations MAY choose to treat TLS passthrough as a special case. Implementations that do this SHOULD clearly
+document their approach if BackendTLSPolicy is treated differently for TLS passthrough.
 
 Note that there are cases where these patterns may result in multiple layers of TLS on a single connection.
-There may be even cases where the gateway implementation is unaware of this; for example, processing TCPRoute traffic -- the traffic may or may not be TLS, and the gateway would be unaware.
+There may be even cases where the gateway implementation is unaware of this; for example, processing TCPRoute traffic --
+the traffic may or may not be TLS, and the gateway would be unaware.
 This is intentional to allow full fidelity of the API, as this is commonly desired for tunneling scenarios.
-When users do not want this, they should ensure that the BackendTLSPolicy is not incorrectly applied to traffic that is already TLS.
+When users do not want this, they should ensure that the BackendTLSPolicy is not incorrectly applied to traffic that is
+already TLS.
 The [Future Plans](#future-plans) include more controls over the API to make this easier to manage.
 
 ## Request Flow
@@ -285,20 +604,26 @@ i.e. Service, in the cluster based on backendRefs rules of the HTTPRoute **and t
 
 ## Future plans
 
-In order to scope this GEP, some some changes are deferred to a near-future GEP.
-This GEP intends to add the ability for additional control by gateway clients to override TLS settings, following previously established patterns of [consumer and producer policies]([glossary](https://gateway-api.sigs.k8s.io/concepts/glossary/?h=gloss#producer-route)).
-Additionally, more contextual control over when to apply the policies will be explored, to enable use cases like "apply TLS only from this route" ([issue](https://github.com/kubernetes-sigs/gateway-api/issues/3856)).
+In order to scope this GEP, some changes are deferred to a near-future GEP.
+This GEP intends to add the ability for additional control by gateway clients to override TLS settings, following previously
+established patterns of [consumer and producer policies]([glossary](https://gateway-api.sigs.k8s.io/concepts/glossary/?h=gloss#producer-route)).
+Additionally, more contextual control over when to apply the policies will be explored, to enable use cases like "apply
+TLS only from this route" ([issue](https://github.com/kubernetes-sigs/gateway-api/issues/3856)).
 
-While the details of these plans are out of scope for this GEP it is important to be aware of the future plans for the API to ensure the immediate-term plans are future-proofed against the proposed plans.
+While the details of these plans are out of scope for this GEP it is important to be aware of the future plans for the
+API to ensure the immediate-term plans are future-proofed against the proposed plans.
 
-Implementations should plan for the existence of future fields that may be added that will control where the TLS policy applies.
+Implementations should plan for the existence of future fields that may be added that will control where the TLS policy
+applies.
 These may include, but are not limited to:
 
 * `spec.targetRefs.namespace`
 * `spec.targetRefs.from`
 * `spec.mode`
 
-While in some cases adding new fields may be seen as a backwards compatibility risk, due to older implementations not knowing to respect the fields, these fields (or similar, should future GEPs decide on new names) are pre-approved to be added in a future release, should the GEPs to add them are approved in the first place.
+While in some cases adding new fields may be seen as a backwards compatibility risk, due to older implementations not
+knowing to respect the fields, these fields (or similar, should future GEPs decide on new names) are pre-approved to be
+added in a future release, should the GEPs to add them are approved in the first place.
 
 ## Alternatives
 Most alternatives are enumerated in the section "The history of backend TLS".  A couple of additional
@@ -396,28 +721,37 @@ developer to provide the appropriate configuration for TLS.  The implementation
 provided by the application and verify that it satisfies the requirements of the route-as-client, including SAN
 information.  Sometimes the backend owner and route owner are the same entity.
 
-A. This was most recently addressed by
-adding hostname for SNI and removing allowed SANs.
+A. This was most recently addressed by adding hostname for SNI and removing allowed SANs.
+
+Q. Rob Scott is interested in extending the TargetRef to optionally include port, since we are targeting the entirety
+of a Service. See the discussion in https://github.com/kubernetes-sigs/gateway-api/pull/2113/files#r1231594914,
+and follow-up issue in https://github.com/kubernetes-sigs/gateway-api/issues/2147.
+
+A. TargetRef has been changed to `LocalPolicyTargetReferenceWithSectionName`, wherein the `SectionName` field is
+interpreted as a port name for a Service.
 
 ## Graduation Criteria
 
 This section is to record issues that were requested for discussion in the API section before this GEP graduates
 out of `Provisional` status.
 
-1. Rob Scott is interested in extending the TargetRef to optionally include port, since we are targeting the entirety
-of a Service. See the discussion in https://github.com/kubernetes-sigs/gateway-api/pull/2113/files#r1231594914,
-and follow up issue in https://github.com/kubernetes-sigs/gateway-api/issues/2147
-2. Michael Pleshakov asked about conflicts that could arise when multiple implementations are running in a cluster.
+1. Michael Pleshakov asked about conflicts that could arise when multiple implementations are running in a cluster.
 This is a gap in our policy attachment model that needs to be addressed.  See the discussion in
 https://github.com/kubernetes-sigs/gateway-api/pull/2113/files#r1235750540. Graduating this GEP to implementable
 requires an update to the Policy GEP to define how status can be nested to support multiple implementations. This will
 likely look very similar to Route status.
-See [comment](https://github.com/kubernetes-sigs/gateway-api/pull/2113#issuecomment-1696127092).
-3. Rob Scott [wanted to note](https://github.com/kubernetes-sigs/gateway-api/pull/2113#issuecomment-1696127092) that
+
+>This question has been converted to a Gateway API enhancement request for Policy:
+https://github.com/kubernetes-sigs/gateway-api/issues/4098.
+
+2. Rob Scott [wanted to note](https://github.com/kubernetes-sigs/gateway-api/pull/2113#issuecomment-1696127092) that
 when this graduates to the standard channel, implementations of HTTPRoute may also be
 required to watch the BackendTLSPolicy. If one of these policies is attached to a Service targeted by an HTTPRoute,
 the implementation would be required to fully implement the policy or mark the backend invalid.
 
+>This comment may be added to the release notes for v1.4.0 of Gateway API, along with other special notes for the introduction
+of the first standard Policy type.
+
 ## References
 
 [Gateway API TLS Use Cases](https://docs.google.com/document/d/17sctu2uMJtHmJTGtBi_awGB0YzoCLodtR6rUNmKMCs8/edit#heading=h.cxuq8vo8pcxm)
diff --git a/geps/gep-1897/metadata.yaml b/geps/gep-1897/metadata.yaml
index 716cf3d0..99d5444a 100644
--- a/geps/gep-1897/metadata.yaml
+++ b/geps/gep-1897/metadata.yaml
@@ -2,12 +2,14 @@ apiVersion: internal.gateway.networking.k8s.io/v1alpha1
 kind: GEPDetails
 number: 1897
 name: BackendTLSPolicy - Explicit Backend TLS Connection Configuration
-status: Experimental
+status: Standard
 authors:
   - candita
   - brianehlert
   - robscott
   - youngnick
+  - howardjohn
+  - snorwin
 relationships:
   obsoletes:
     - number: 1282
diff --git a/geps/gep-696/index.md b/geps/gep-696/index.md
index 6082b309..da58798f 100644
--- a/geps/gep-696/index.md
+++ b/geps/gep-696/index.md
@@ -13,16 +13,27 @@
 
 (Primary goals of this proposal.)
 
+## Longer Term Goals (optional)
+
+(goals that are not covered initially on this proposal but may be considered long term)
+
 ## Non-Goals
 
 (What is out of scope for this proposal.)
 
-## Introduction
+## Introduction/Overview
 
 (Can link to external doc -- but we should bias towards copying
 the content into the GEP as online documents are easier to lose
 -- e.g. owner messes up the permissions, accidental deletion)
 
+Write here "What" we want to do. What is the proposal aiming to do?
+
+## Purpose (Why and Who)
+
+Write here "Why" we want to do it. What problems are being solved? What personas are
+the target of this proposal, and why will this proposal will make their lives better?
+
 ## API
 (... details, can point to PR with changes)
 
@@ -32,6 +43,17 @@ the content into the GEP as online documents are easier to lose
 ### Gateway For Mesh (East/West)
 (Include East/West API considerations, examples, and if different - APIs)
 
+## Request flow
+Example on the usage flow of this proposal/enhancement. It is suggested to contain
+at least one manifest as example.
+
+Example of a flow description:
+
+* A client makes a request to https://foo.example.com.
+* DNS resolves the name to a `Gateway` address.
+* The reverse proxy receives the request on a `Listener` and does something with it
+* The reverse proxy passes the request through `XPTORoute` modifying the headers to contain `XYZ`
+
 ## Conformance Details
 
 (from https://github.com/kubernetes-sigs/gateway-api/blob/main/geps/gep-2162/index.md#standardize-features-and-conformance-tests-names)
@@ -60,6 +82,11 @@ In any case, the conformance tests file names should be meaningful and easy to u
 
 (Make sure to also include conformance tests that cover mesh)
 
+When describing the new feature, write down some conformance test scenarios the feature should manage,
+to guarantee that future implementors understand what "Conformance" means and what will be tested.
+
+At least _some_ tests should be added at each phase, starting with Provisional.
+
 ## Alternatives
 
 (List other design alternatives and why we did not go in that
diff --git a/nav.yml b/nav.yml
index dd16ff32..a51849e3 100644
--- a/nav.yml
+++ b/nav.yml
@@ -72,7 +72,6 @@ nav:
       - geps/gep-1731/index.md
       - geps/gep-1748/index.md
       - geps/gep-1767/index.md
-      - geps/gep-1897/index.md
       - geps/gep-2162/index.md
       - geps/gep-3155/index.md
     - Standard:
@@ -93,6 +92,7 @@ nav:
       - geps/gep-1742/index.md
       - geps/gep-1762/index.md
       - geps/gep-1867/index.md
+      - geps/gep-1897/index.md
       - geps/gep-1911/index.md
       - geps/gep-2257/index.md
       - geps/gep-3171/index.md
diff --git a/pkg/generated/openapi/zz_generated.openapi.go b/pkg/generated/openapi/zz_generated.openapi.go
index b11d00af..34b5ca19 100644
--- a/pkg/generated/openapi/zz_generated.openapi.go
+++ b/pkg/generated/openapi/zz_generated.openapi.go
@@ -4269,7 +4269,7 @@ func schema_sigsk8sio_gateway_api_apis_v1_GatewayClassStatus(ref common.Referenc
 							},
 						},
 						SchemaProps: spec.SchemaProps{
-							Description: "SupportedFeatures is the set of features the GatewayClass support. It MUST be sorted in ascending alphabetical order by the Name key. <gateway:experimental>",
+							Description: "SupportedFeatures is the set of features the GatewayClass support. It MUST be sorted in ascending alphabetical order by the Name key.",
 							Type:        []string{"array"},
 							Items: &spec.SchemaOrArray{
 								Schema: &spec.Schema{



Test patch:
diff --git a/conformance/tests/backendtlspolicy-observed-generation-bump.go b/conformance/tests/backendtlspolicy-observed-generation-bump.go
index 83c12378..85f44a70 100644
--- a/conformance/tests/backendtlspolicy-observed-generation-bump.go
+++ b/conformance/tests/backendtlspolicy-observed-generation-bump.go
@@ -39,7 +39,7 @@ func init() {
 
 var BackendTLSPolicyObservedGenerationBump = suite.ConformanceTest{
 	ShortName:   "BackendTLSPolicyObservedGenerationBump",
-	Description: "A BackendTLSPolicy in the gateway-conformance-infra namespace should update the observedGeneration in all of it's Status.Conditions after an update to the spec",
+	Description: "A BackendTLSPolicy in the gateway-conformance-infra namespace should update the observedGeneration in all of its Status.Conditions after an update to the spec",
 	Features: []features.FeatureName{
 		features.SupportGateway,
 		features.SupportHTTPRoute,
diff --git a/conformance/tests/backendtlspolicy-observed-generation-bump.yaml b/conformance/tests/backendtlspolicy-observed-generation-bump.yaml
index e7d4d40c..8a46c8ea 100644
--- a/conformance/tests/backendtlspolicy-observed-generation-bump.yaml
+++ b/conformance/tests/backendtlspolicy-observed-generation-bump.yaml
@@ -41,5 +41,5 @@ spec:
       - group: ""
         kind: ConfigMap
         # This ConfigMap is generated dynamically by the test suite.
-        name: "backend-tls-checks-certificate"
+        name: "tls-checks-ca-certificate"
     hostname: "abc.example.com"
diff --git a/conformance/tests/grpcroute-named-rule.go b/conformance/tests/grpcroute-named-rule.go
index 2c738a56..c5b80ab6 100644
--- a/conformance/tests/grpcroute-named-rule.go
+++ b/conformance/tests/grpcroute-named-rule.go
@@ -35,7 +35,7 @@ func init() {
 
 var GRPCRouteNamedRule = suite.ConformanceTest{
 	ShortName:   "GRPCRouteNamedRule",
-	Description: "An GRPCRoute with a named GRPCRouteRule",
+	Description: "A GRPCRoute with a named GRPCRouteRule",
 	Manifests:   []string{"tests/grpcroute-named-rule.yaml"},
 	Features: []features.FeatureName{
 		features.SupportGateway,
diff --git a/conformance/tests/grpcroute-weight.go b/conformance/tests/grpcroute-weight.go
index ba5a4d56..64434912 100644
--- a/conformance/tests/grpcroute-weight.go
+++ b/conformance/tests/grpcroute-weight.go
@@ -38,7 +38,7 @@ func init() {
 
 var GRPCRouteWeight = suite.ConformanceTest{
 	ShortName:   "GRPCRouteWeight",
-	Description: "An GRPCRoute with weighted backends",
+	Description: "A GRPCRoute with weighted backends",
 	Manifests:   []string{"tests/grpcroute-weight.yaml"},
 	Features: []features.FeatureName{
 		features.SupportGateway,
diff --git a/conformance/utils/suite/suite_test.go b/conformance/utils/suite/suite_test.go
index 778afa71..674e7480 100644
--- a/conformance/utils/suite/suite_test.go
+++ b/conformance/utils/suite/suite_test.go
@@ -28,6 +28,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client/fake"
 
 	gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
+	xmeshv1alpha1 "sigs.k8s.io/gateway-api/apisx/v1alpha1"
 	confv1 "sigs.k8s.io/gateway-api/conformance/apis/v1"
 	"sigs.k8s.io/gateway-api/pkg/consts"
 	"sigs.k8s.io/gateway-api/pkg/features"
@@ -411,7 +412,7 @@ func TestSuiteReport(t *testing.T) {
 	}
 }
 
-var statusFeatureNames = []string{
+var gwcStatusFeatureNames = []string{
 	"Gateway",
 	"GatewayPort8080",
 	"HTTPRoute",
@@ -423,7 +424,7 @@ var statusFeatureNames = []string{
 	"ReferenceGrant",
 }
 
-func TestInferSupportedFeatures(t *testing.T) {
+func TestInferGWCSupportedFeatures(t *testing.T) {
 	testCases := []struct {
 		name               string
 		allowAllFeatures   bool
@@ -435,7 +436,7 @@ func TestInferSupportedFeatures(t *testing.T) {
 	}{
 		{
 			name:             "properly infer supported features",
-			expectedFeatures: namesToFeatureSet(statusFeatureNames),
+			expectedFeatures: namesToFeatureSet(gwcStatusFeatureNames),
 			expectedSource:   supportedFeaturesSourceInferred,
 		},
 		{
@@ -460,7 +461,7 @@ func TestInferSupportedFeatures(t *testing.T) {
 		{
 			name:               "supports conformance profile - core",
 			ConformanceProfile: sets.New(GatewayHTTPConformanceProfileName),
-			expectedFeatures:   namesToFeatureSet(statusFeatureNames),
+			expectedFeatures:   namesToFeatureSet(gwcStatusFeatureNames),
 			expectedSource:     supportedFeaturesSourceInferred,
 		},
 	}
@@ -482,7 +483,7 @@ func TestInferSupportedFeatures(t *testing.T) {
 					Message: "GatewayClass is accepted and ready for use",
 				},
 			},
-			SupportedFeatures: featureNamesToSet(statusFeatureNames),
+			SupportedFeatures: featureNamesToSet(gwcStatusFeatureNames),
 		},
 	}
 	scheme := runtime.NewScheme()
@@ -524,6 +525,113 @@ func TestInferSupportedFeatures(t *testing.T) {
 	}
 }
 
+var meshStatusFeatureNames = []string{
+	"Mesh",
+	"MeshClusterIPMatching",
+	"MeshNamespaceSelector",
+	"MeshServiceAccountSelector",
+	"MeshTLS",
+	"MeshTLSClientCert",
+	"MeshTrafficSplit",
+	"MeshAccessControl",
+	"HTTPRoute",
+}
+
+func TestXMeshInferSupportedFeatures(t *testing.T) {
+	testCases := []struct {
+		name               string
+		allowAllFeatures   bool
+		supportedFeatures  FeaturesSet
+		exemptFeatures     FeaturesSet
+		ConformanceProfile sets.Set[ConformanceProfileName]
+		expectedFeatures   FeaturesSet
+		expectedSource     supportedFeaturesSource
+	}{
+		{
+			name:             "properly infer mesh supported features",
+			expectedFeatures: namesToFeatureSet(meshStatusFeatureNames),
+			expectedSource:   supportedFeaturesSourceInferred,
+		},
+		{
+			name:              "no features",
+			supportedFeatures: sets.New[features.FeatureName]("Mesh"),
+			expectedFeatures:  sets.New[features.FeatureName]("Mesh"),
+			expectedSource:    supportedFeaturesSourceManual,
+		},
+		{
+			name:              "remove exempt features",
+			supportedFeatures: sets.New[features.FeatureName]("MeshTLS", "MeshAccessControl"),
+			exemptFeatures:    sets.New[features.FeatureName]("MeshAccessControl"),
+			expectedFeatures:  sets.New[features.FeatureName]("MeshTLS"),
+			expectedSource:    supportedFeaturesSourceManual,
+		},
+		{
+			name:               "supports conformance profile - core",
+			ConformanceProfile: sets.New(MeshHTTPConformanceProfileName),
+			expectedFeatures:   namesToFeatureSet(meshStatusFeatureNames),
+			expectedSource:     supportedFeaturesSourceInferred,
+		},
+	}
+
+	meshName := "xochopintre"
+	xmesh := &xmeshv1alpha1.XMesh{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: meshName,
+		},
+		Spec: xmeshv1alpha1.MeshSpec{
+			ControllerName: "example.com/mesh-controller",
+		},
+		Status: xmeshv1alpha1.MeshStatus{
+			Conditions: []metav1.Condition{
+				{
+					Type:    string(xmeshv1alpha1.MeshConditionAccepted),
+					Status:  metav1.ConditionTrue,
+					Reason:  "Accepted",
+					Message: "XMesh is accepted and ready for use",
+				},
+			},
+			SupportedFeatures: featureNamesToSet(meshStatusFeatureNames),
+		},
+	}
+	scheme := runtime.NewScheme()
+	scheme.AddKnownTypes(xmeshv1alpha1.SchemeGroupVersion, &xmeshv1alpha1.XMesh{})
+	fakeClient := fake.NewClientBuilder().
+		WithScheme(scheme).
+		WithObjects(xmesh).
+		WithLists(&apiextensionsv1.CustomResourceDefinitionList{}).
+		Build()
+
+	xmeshv1alpha1.Install(fakeClient.Scheme())
+	apiextensionsv1.AddToScheme(fakeClient.Scheme())
+
+	for _, tc := range testCases {
+		options := ConformanceOptions{
+			AllowCRDsMismatch:          true,
+			MeshName:                   meshName,
+			EnableAllSupportedFeatures: tc.allowAllFeatures,
+			SupportedFeatures:          tc.supportedFeatures,
+			ExemptFeatures:             tc.exemptFeatures,
+			ConformanceProfiles:        tc.ConformanceProfile,
+			Client:                     fakeClient,
+		}
+
+		t.Run(tc.name, func(t *testing.T) {
+			cSuite, err := NewConformanceTestSuite(options)
+			if err != nil {
+				t.Fatalf("error initializing conformance suite: %v", err)
+			}
+
+			if cSuite.supportedFeaturesSource != tc.expectedSource {
+				t.Errorf("InferredSupportedFeatures mismatch: got %v, want %v", cSuite.supportedFeaturesSource, tc.expectedSource)
+			}
+
+			if equal := cSuite.SupportedFeatures.Equal(tc.expectedFeatures); !equal {
+				t.Errorf("SupportedFeatures mismatch: got %v, want %v", cSuite.SupportedFeatures.UnsortedList(), tc.expectedFeatures.UnsortedList())
+			}
+		})
+	}
+}
+
 func TestGWCPublishedMeshFeatures(t *testing.T) {
 	gwcName := "ochopintre"
 	gwc := &gatewayv1.GatewayClass{