Skip to content

Gateway API — supported features (aether)

Where conformance stands

GATEWAY-HTTP (north-south) — Core 36/36 and Extended 10/10. Measured on a real cluster against the upstream suite at v1.6.3 by rev22 (2026-10-11), the newest run in the archive. The same profile runs nightly in CI at v1.6.3, where .github/workflows/e2e.yaml calls it a "Hard gate (46/46 on kind at Gateway API v1.6.3: Core 36, Extended 10)".

MESH-HTTP (east-west GAMMA) — a hard CI gate at Core 7/7. The nightly workflow runs the mesh profile through the full capture path on its own cluster, and a regression fails it. On a real cluster against the upstream suite at v1.6.3, rev22 (2026-10-11) measured MESH-HTTP Core 7/7.

rev22 (2026-10-11) states its own caveat, and it is still the right way to read this page: Do not read this doc as a claim that every Gateway API feature is implemented: the two profiles pass at the features aether advertises, and the 29 Extended features it does not advertise are skipped, not passed.

Aether implements Gateway API natively on its own SPIRE-mTLS + registry-EDS data plane (proposal 018), both north-south (edge Gateway/HTTPRoute) and east-west (GAMMA, HTTPRoute/GRPCRoute with parentRef: Service). This is the honest supported-features list — "ship support first, report it honestly, chase the badge later". Supported = implemented and e2e-validated on the reference cluster; Partial = implemented with the noted limitation; Planned = on the 018 roadmap, not yet shipped.

The L4 routes (TCPRoute/TLSRoute/UDPRoute) and the TCP-over-mTLS floor below were first e2e-validated on the reference cluster at aether chart 0.41.0 and have been carried by every release since (chart 0.92.x at the time of writing); the conformance jobs in .github/workflows/e2e.yaml are the standing gate.

Route types

Feature Status Notes
HTTPRoute (edge, parentRef=Gateway) Supported path + host matches, wildcard TLS; backendRef requires port (use the service default)
HTTPRoute (GAMMA, parentRef=Service) Supported path/header matches, weighted backendRefs, per-rule request timeout; producer routes
GRPCRoute (GAMMA) Supported method match → /<service>/<method>; service-only → prefix; header matches; weighted backends
TCPRoute (edge, parentRef=Gateway) Supported raw TCP through the edge LB → backend over the TCP floor; e2e-validated
TLSRoute (edge) Supported TLS passthrough — tls_inspector reads SNI for routing, the edge does NOT terminate TLS (validated: client sees the backend's cert); per-SNI server_names filter chains
UDPRoute (east-west, parentRef=Service) Supported, with limits udp_proxy floor to the backend's app UDP port. NOTE: UDP is plaintext — mesh mTLS does not cover the UDP floor (DTLS not implemented). No traffic splitting: udp_proxy's only route action carries a single cluster (there is no weighted-cluster equivalent of tcp_proxy's), so a multi-backend UDPRoute binds the HEAVIEST backend and the split is discarded. One UDPRoute-backed service per pod: the capture redirect destroys the destination ClusterIP, so the per-pod UDP listener cannot tell services apart. An explicit weight: 0 IS honoured as a drain. Every discard is logged and counted on aether.agent.l4route.udp_unsupported — see #873

Routing vocabulary (HTTP/gRPC)

Feature Status Notes
Path match (Exact, PathPrefix) Supported
Header match (Exact) Supported
Method match (gRPC) Supported
Weighted backends (canary/split) Supported WeightedClusters
Request timeout Supported HTTPRoute timeouts.request
Request mirroring Planned
RequestRedirect filter Supported scheme → SchemeRedirect; hostname → HostRedirect; port → PortRedirect; statusCode 301/302 → MOVED_PERMANENTLY/FOUND; path.ReplaceFullPath → PathRedirect; path.ReplacePrefixMatch → PrefixRewrite; yields Route_Redirect (no backend); applied on HTTPRoute (edge + GAMMA)
URLRewrite filter Supported hostname → HostRewriteLiteral; path.ReplacePrefixMatch → PrefixRewrite; path.ReplaceFullPath → RegexRewrite (.* → value); applied on HTTPRoute (edge + GAMMA)
RequestHeaderModifier filter Supported set → OVERWRITE_IF_EXISTS_OR_ADD; add → APPEND_IF_EXISTS_OR_ADD; remove → request_headers_to_remove; applied at route level on HTTPRoute (edge + GAMMA) and GRPCRoute
ResponseHeaderModifier filter Supported same shape on response_headers_to_add / response_headers_to_remove; applied at route level on HTTPRoute (edge + GAMMA) and GRPCRoute
RegularExpression matches Supported gRPC method RegularExpression type → Envoy safe_regex path (/<serviceRegex>/<methodRegex>); unset component uses [^/]+

Mesh & multi-cluster

Feature Status Notes
GAMMA Mesh profile (parentRef=Service) Supported producer routes; consumer (per-namespace) overrides Planned
GAMMA rules on the transparent-capture path Supported applies to clients dialing <svc>.<ns>.<meshDomain> (the default path)
East-west L4 on the transparent-capture path Supported TCPRoute/TLSRoute/UDPRoute (parentRef=Service) project onto the capture floor; unconditional since proposal 031 (the --l4-routes flag was retired), gated only on the route CRDs being installed
ReferenceGrant (cross-namespace backendRefs) Supported (admission + status + per-namespace resolution) A cross-namespace backendRef (namespace set and != the route's) is admitted only when a ReferenceGrant in the backend's namespace has a from matching {group: gateway.networking.k8s.io, kind: <route kind>, namespace: <route ns>} and a to matching {group: "", kind: Service} (optionally name). Without a grant the route's ResolvedRefs is False/RefNotPermitted and the backend is DROPPED from the data plane (rest of the route still applies). Enforced on edge HTTPRoute/TCPRoute/TLSRoute and east-west GAMMA HTTPRoute/GRPCRoute + L4 TCPRoute/TLSRoute/UDPRoute. The data-plane cluster name is namespace-qualified (<svc>.<ns>.<meshDomain>) since the proposal 020 cutover, so a granted cross-ns ref resolves to the backend's own namespace
MCS ServiceExport/ServiceImport, clusterset.local Supported (phase 1) registry-backed (proposals 018 + 006), DNS strictly local. registrar.enableMCS=true exports local ServiceExports to the registry and materializes peer-exported services as ServiceImports + clusterset VIPs. Requires the etcd backend + the MCS-API CRDs

Transport / security

Feature Status Notes
Listener TLS termination (edge) Supported SDS; wildcard certs
Upstream mTLS (SPIRE) on every hop Supported per-endpoint SPIFFE SAN from registry EDS
TCP-over-mTLS passthrough (non-HTTP) Supported Phase 3a floor: per-source SPIRE mTLS, the inbound default TCP floor chain, and protocol-aware TCP-connect liveness for non-HTTP apps

Status reporting

GatewayClass/Gateway/Route status-condition reporting (Accepted/Programmed/ResolvedRefs) is Supported: the edge controller is namespace-agnostic — it reconciles every Gateway of its GatewayClass in ANY namespace and publishes the Gateway/Route status conditions and per-listener attachedRoutes, which is what the upstream conformance suite's NamespacesMustBeReady gate requires before any test runs.

Every class-aether Gateway also gets status.addresses (one IPAddress entry), so the suite's "wait for at least one IP address in status" setup step passes and the GATEWAY-HTTP traffic tests run. Each Gateway gets its own LoadBalancer Service and therefore its own distinct address (proposal 021 Phase 2, unconditional since 031 round 2); the shared-address fallback of Phase 1 remains only when the edge Service name is unset or port allocation fails. The address is resolved at runtime from the Service's status (robust to MetalLB assignment): until the LB IP is assigned the address is omitted rather than written empty.

The GatewayClass also publishes a machine-readable status.supportedFeatures list so the suite skips (rather than fails) the features aether does not implement. The advertised set (sorted) is: Gateway, GatewayPort8080, HTTPRoute, HTTPRouteHostRewrite, HTTPRouteMethodMatching, HTTPRoutePathRedirect, HTTPRoutePathRewrite, HTTPRoutePortRedirect, HTTPRouteRequestTimeout, HTTPRouteResponseHeaderModification, HTTPRouteSchemeRedirect, ReferenceGrant. Path/header/method match, weighted backends, and the RequestHeaderModifier filter are part of HTTPRoute core and carry no separate feature flag. The RequestRedirect filter (HTTPRoutePortRedirect/HTTPRouteSchemeRedirect/HTTPRoutePathRedirect) and URLRewrite filter (HTTPRouteHostRewrite/HTTPRoutePathRewrite) are advertised now that both are implemented on edge + GAMMA HTTPRoute; host redirect carries no separate flag (host+status is core), and only the 301/302 redirect status codes are implemented (the 303/307/308 status-code features are not advertised). Request mirroring is deliberately omitted (not implemented). ReferenceGrant is advertised now that cross-namespace backendRef admission, status enforcement, and namespace-qualified resolution (proposal 020) are all implemented.

GRPCRoute is not advertised on the GatewayClass: aether serves GRPCRoute only east-west via GAMMA (parentRef: Service), which is a mesh feature with no GatewayClass, so the north-south Gateway does not serve GRPCRoute. Advertising SupportGRPCRoute on the GatewayClass makes the upstream GATEWAY-GRPC suite send gRPC traffic through a Gateway the edge cannot serve (the tests run and fail/timeout), so it is omitted here while GAMMA continues to implement GRPCRoute for the mesh profile (see "Route types" above).

GatewayInfrastructure is not advertised, although both things spec.infrastructure asks for are implemented:

  • spec.infrastructure.labels and annotations are propagated to the resource the edge generates for a Gateway, its per-Gateway LoadBalancer Service, with update and removal following the Gateway (docs/configuration.md, "Labelling and annotating a Gateway's LoadBalancer Service").
  • A Gateway whose spec.infrastructure.parametersRef is invalid or does not resolve is rejected with Accepted=False/InvalidParameters.

The reason is where the generated resource lives. The suite's GatewayInfrastructureMetadata test looks in the Gateway's own namespace for a ServiceAccount, Pod or Service that carries the label gateway.networking.k8s.io/gateway-name=<gateway>, waits until it finds one, and checks the Gateway's labels and annotations on it. That is the model of an implementation that deploys a proxy per Gateway, next to the Gateway. Aether runs one shared edge Deployment for every Gateway, and the single resource it generates per Gateway is a Service in the edge namespace, because a Service can only select pods of its own namespace. The test therefore finds nothing in the Gateway's namespace and times out, however correctly the Service is labelled. The edge does not put the gateway-name label on that Service, and refuses it from spec.infrastructure, so that nothing claims a placement that is not true.

Since gateway-api v1.6.3 the suite gates both GatewayInfrastructureMetadata and GatewayInvalidParametersRef on that one feature, so both are skipped. GatewayInvalidParametersRef was a Core test through v1.6.2 and passed there; the rejection is unchanged and, like the propagation, is covered by the edge reconciler's unit tests.

Passing would need the per-Gateway Service to live in the Gateway's namespace: a Service without a selector, with EndpointSlices the edge controller writes from the edge pods' addresses. That brings an RBAC change (cluster-wide EndpointSlice writes), a migration of the addresses existing Gateways hold (a new Service is assigned a new address unless it is pinned), and a moved trust boundary (whoever can edit Services in the Gateway's namespace could then edit the load balancer's Service, which only the edge namespace can do today). It is recorded here as a design option. It is not planned.

Data-plane / addressing

Per-Gateway addressing (proposal 021 Phase 2) is implemented and unconditional: each class-aether Gateway gets its own LoadBalancer Service and its own distinct address, published in status.addresses, with internal-port demux behind the one edge Deployment. This satisfies both the address-dependent GATEWAY-HTTP traffic tests and the tests asserting each Gateway has its OWN address. The Phase 1 shared-address behavior survives only as the fallback for when the edge Service name is unset or a per-Gateway port cannot be allocated.