Repository: kiali/kiali
Pull request #7991 (2024-12-11 10:31:41)

Handle URL in kiosk for time duration modal
### Describe the change

Handle URL in kiosk for time duration modal

### Steps to test the PR

Go to: http://localhost:3000/console/namespaces/bookinfo/workloads/productpage-v1?duration=604800&refresh=30000&kiosk=true&tab=logs&rangeDuration=604800

Make sure that the selector is: 
![image](https://github.com/user-attachments/assets/c948ee37-fd80-4008-b001-1523f7a3bde4)

Also in the modal: 
![image](https://github.com/user-attachments/assets/72413a0f-2092-4a1e-a61b-9031b7630ad2)

Update the modal data: 
![image](https://github.com/user-attachments/assets/14e08f74-79ab-4022-86a1-b394ff976460)

Make sure it changes in the URL. 

Note: For Overview & Traffic, the duration parameter is used. For the other tabs, rangeDuration. Where rangeDuration is used, the "to" and "from" parameters can also be used:

http://localhost:3000/console/namespaces/bookinfo/workloads/productpage-v1?duration=604800&refresh=60000&kiosk=true&tab=traces&from=1733912384867&to=1733912700115

![image](https://github.com/user-attachments/assets/b5705ad7-3331-42ff-8491-107e18bb23eb)


### Automation testing

I think this will be validated in OSSMC tests (Can be done in a follow up PR)

### Issue reference

Fixes https://github.com/kiali/kiali/issues/7958



Patch:
diff --git a/business/checkers/virtualservices/route_checker.go b/business/checkers/virtualservices/route_checker.go
index 08e9ae484..0c8804f22 100644
--- a/business/checkers/virtualservices/route_checker.go
+++ b/business/checkers/virtualservices/route_checker.go
@@ -88,7 +88,8 @@ func (route RouteChecker) checkTcpRoutes() ([]*models.IstioCheck, bool) {
 				continue
 			}
 			weight := destinationWeights[0].Weight
-			if weight < 100 {
+			// We can't rely on nil value as Weight is an integer that will be always present
+			if weight > 0 && weight < 100 {
 				valid = true
 				path := fmt.Sprintf("spec/tcp[%d]/route[%d]/weight", routeIdx, 0)
 				validation := models.Build("virtualservices.route.singleweight", path)
@@ -119,7 +120,8 @@ func (route RouteChecker) checkTlsRoutes() ([]*models.IstioCheck, bool) {
 				continue
 			}
 			weight := destinationWeights[0].Weight
-			if weight < 100 {
+			// We can't rely on nil value as Weight is an integer that will be always present
+			if weight > 0 && weight < 100 {
 				valid = true
 				path := fmt.Sprintf("spec/tls[%d]/route[%d]/weight", routeIdx, 0)
 				validation := models.Build("virtualservices.route.singleweight", path)
diff --git a/business/proxy_status.go b/business/proxy_status.go
index e094a4147..691172581 100644
--- a/business/proxy_status.go
+++ b/business/proxy_status.go
@@ -15,26 +15,32 @@ type ProxyStatusService struct {
 	businessLayer  *Layer
 }
 
-func (in *ProxyStatusService) GetPodProxyStatus(cluster, ns, pod string) *models.ProxyStatus {
-	return castProxyStatus(kialiCache.GetPodProxyStatus(cluster, ns, pod))
+// GetPodProxyStatus isSubscribed is used to return IGNORED if sent is empty, instead of NOT_SENT
+func (in *ProxyStatusService) GetPodProxyStatus(cluster, ns, pod string, isSubscribed bool) *models.ProxyStatus {
+	return castProxyStatus(kialiCache.GetPodProxyStatus(cluster, ns, pod), isSubscribed)
 }
 
-func castProxyStatus(ps *kubernetes.ProxyStatus) *models.ProxyStatus {
+// castProxyStatus returns a status string depending on the proxyStatus and whether the proxy is subscribed
+// See https://github.com/istio/istio/pull/51638/files#diff-fded610aca2639111f0d6b42e18dfc1ce047126340a2d36bb976cfa4c575b984R8
+func castProxyStatus(ps *kubernetes.ProxyStatus, isSubscribed bool) *models.ProxyStatus {
 	if ps == nil {
 		return nil
 	}
 
 	return &models.ProxyStatus{
-		CDS: xdsStatus(ps.ClusterSent, ps.ClusterAcked),
-		EDS: xdsStatus(ps.EndpointSent, ps.EndpointAcked),
-		LDS: xdsStatus(ps.ListenerSent, ps.ListenerAcked),
-		RDS: xdsStatus(ps.RouteSent, ps.RouteAcked),
+		CDS: xdsStatus(ps.ClusterSent, ps.ClusterAcked, isSubscribed),
+		EDS: xdsStatus(ps.EndpointSent, ps.EndpointAcked, isSubscribed),
+		LDS: xdsStatus(ps.ListenerSent, ps.ListenerAcked, isSubscribed),
+		RDS: xdsStatus(ps.RouteSent, ps.RouteAcked, isSubscribed),
 	}
 }
 
-func xdsStatus(sent, acked string) string {
+func xdsStatus(sent, acked string, isSubscribed bool) string {
 	if sent == "" {
-		return "NOT_SENT"
+		if isSubscribed {
+			return "NOT_SENT"
+		}
+		return "IGNORED"
 	}
 	if sent == acked {
 		return "Synced"
diff --git a/business/services.go b/business/services.go
index b0eb556aa..b948f10f8 100644
--- a/business/services.go
+++ b/business/services.go
@@ -582,6 +582,7 @@ func (in *SvcService) GetServiceDetails(ctx context.Context, cluster, namespace,
 		wi := &models.WorkloadListItem{}
 		wi.ParseWorkload(w)
 		wo = append(wo, wi)
+		// The service is not marked as Ambient if any of the workloads is Ambient
 		if !w.IsAmbient {
 			isAmbient = false
 		}
diff --git a/business/workloads.go b/business/workloads.go
index 08845a99d..2eb7b94bd 100644
--- a/business/workloads.go
+++ b/business/workloads.go
@@ -286,6 +286,9 @@ func (in *WorkloadService) GetWorkloadList(ctx context.Context, criteria Workloa
 		}
 		wItem.Cluster = cluster
 		wItem.Namespace = namespace
+		if w.IsWaypoint() {
+			wItem.Ambient = "waypoint"
+		}
 		workloadList.Workloads = append(workloadList.Workloads, *wItem)
 	}
 
@@ -1322,8 +1325,8 @@ func (in *WorkloadService) fetchWorkloadsFromCluster(ctx context.Context, cluste
 
 		// Add the Proxy Status to the workload
 		for _, pod := range w.Pods {
-			if pod.HasIstioSidecar() && !w.IsGateway() && config.Get().ExternalServices.Istio.IstioAPIEnabled {
-				pod.ProxyStatus = in.businessLayer.ProxyStatus.GetPodProxyStatus(cluster, namespace, pod.Name)
+			if config.Get().ExternalServices.Istio.IstioAPIEnabled && (pod.HasIstioSidecar() || w.IsWaypoint()) {
+				pod.ProxyStatus = in.businessLayer.ProxyStatus.GetPodProxyStatus(cluster, namespace, pod.Name, !w.IsWaypoint())
 			}
 		}
 
@@ -1918,18 +1921,34 @@ func (in *WorkloadService) fetchWorkload(ctx context.Context, criteria WorkloadC
 
 		// Add the Proxy Status to the workload
 		for _, pod := range w.Pods {
-			if pod.HasIstioSidecar() && !w.IsGateway() && config.Get().ExternalServices.Istio.IstioAPIEnabled {
-				pod.ProxyStatus = in.businessLayer.ProxyStatus.GetPodProxyStatus(criteria.Cluster, criteria.Namespace, pod.Name)
+			if config.Get().ExternalServices.Istio.IstioAPIEnabled && (pod.HasIstioSidecar() || w.IsWaypoint()) {
+				pod.ProxyStatus = in.businessLayer.ProxyStatus.GetPodProxyStatus(criteria.Cluster, criteria.Namespace, pod.Name, !w.IsWaypoint())
 			}
 			// If Ambient is enabled for pod, check if has any Waypoint proxy
 			if pod.AmbientEnabled() {
 				w.WaypointWorkloads = in.getWaypointsForWorkload(ctx, criteria.Namespace, w)
+				// TODO: Maybe user doesn't have permissions
+				ztunnelPods := in.cache.GetZtunnelPods(criteria.Cluster)
+				for _, zPod := range ztunnelPods {
+					// There should be a ztunnel pod per node
+					// TODO: Maybe we should check if some of the config differs for one pod?
+					zPodConfig := in.cache.GetZtunnelDump(criteria.Cluster, zPod.Namespace, zPod.Name)
+					if zPodConfig != nil {
+						w.AddPodsProtocol(*zPodConfig)
+					}
+				}
+
 			}
-			// If the pod is a waypoint proxy, check if it is attached to a namespace or to a service account, and get the affected workloads
-			if pod.IsWaypoint() {
-				// Get waypoint workloads
-				w.WaypointWorkloads = append(w.WaypointWorkloads, in.listWaypointWorkloads(ctx, pod.Name, criteria.Cluster)...)
-			}
+		}
+
+		// If the pod is a waypoint proxy, check if it is attached to a namespace or to a service account, and get the affected workloads
+		if w.IsWaypoint() {
+			w.Ambient = "waypoint"
+			// Get waypoint workloads
+			w.WaypointWorkloads = append(w.WaypointWorkloads, in.listWaypointWorkloads(ctx, w.Name, criteria.Cluster)...)
+		}
+		if w.IsZtunnel() {
+			w.Ambient = "ztunnel"
 		}
 
 		if cnFound {
@@ -1939,6 +1958,12 @@ func (in *WorkloadService) fetchWorkload(ctx context.Context, criteria WorkloadC
 	return wl, kubernetes.NewNotFound(criteria.WorkloadName, "Kiali", "Workload")
 }
 
+func (in *WorkloadService) GetZtunnelConfig(cluster, namespace, pod string) *kubernetes.ZtunnelConfigDump {
+
+	return in.cache.GetZtunnelDump(cluster, namespace, pod)
+
+}
+
 // GetWaypoints: Return the list of workloads when the waypoint proxy is applied per namespace
 func (in *WorkloadService) GetWaypoints(ctx context.Context) models.Workloads {
 	if !in.cache.IsWaypointListExpired() {
@@ -1950,15 +1975,15 @@ func (in *WorkloadService) GetWaypoints(ctx context.Context) models.Workloads {
 	waypoints := []*models.Workload{}
 
 	for cluster := range in.userClients {
-		nslist, errNs := in.userClients[cluster].GetNamespaces("")
+		nslist, errNs := in.businessLayer.Namespace.GetClusterNamespaces(ctx, cluster)
 		if errNs != nil {
-			log.Errorf("GetWaypoints: Error fetching namespaces for cluster %s", cluster)
+			log.Errorf("GetWaypoints: Error fetching namespaces for cluster %s. %s", cluster, errNs.Error())
 		}
 
 		for _, ns := range nslist {
 			nsWaypoints, err := in.fetchWorkloadsFromCluster(ctx, cluster, ns.Name, labelSelector)
 			if err != nil {
-				log.Debugf("GetWaypoints: Error fetching workloads for namespace %s, labelSelector %s", ns.Name, labelSelector)
+				log.Debugf("GetWaypoints: Error fetching workloads for namespace %s, labelSelector %s. %s", ns.Name, labelSelector, err.Error())
 				continue
 			}
 			waypoints = append(waypoints, nsWaypoints...)
@@ -2071,8 +2096,10 @@ func (in *WorkloadService) listWaypointWorkloads(ctx context.Context, name, clus
 			log.Debugf("listWaypointWorkloads: Error fetching workloads for namespace %s", ns.Name)
 		}
 		for _, wk := range workloadList {
-			// Is there any annotation that disables?
-			workloadslist = append(workloadslist, *wk)
+			// This annotation disables other labels (Like the ns one)
+			if wk.Labels[in.config.IstioLabels.AmbientNamespaceLabel] != "none" {
+				workloadslist = append(workloadslist, *wk)
+			}
 		}
 
 	}
@@ -2207,6 +2234,10 @@ func (in *WorkloadService) GetWorkloadAppName(ctx context.Context, cluster, name
 		return "", err
 	}
 
+	if wkd.IsGateway() || wkd.IsWaypoint() {
+		// Waypoints and Gateways doesn't have an app label, but they have valid traces data
+		return workload, nil
+	}
 	appLabelName := in.config.IstioLabels.AppLabelName
 	app := wkd.Labels[appLabelName]
 	return app, nil
diff --git a/frontend/public/locales/zh/translation.json b/frontend/public/locales/zh/translation.json
index 709b8abc7..785e87fcd 100644
--- a/frontend/public/locales/zh/translation.json
+++ b/frontend/public/locales/zh/translation.json
@@ -38,6 +38,7 @@
   "Add Source to From List": "Add Source to From List",
   "Additional Conditions of a Request": "Additional Conditions of a Request",
   "Additional State": "Additional State",
+  "Address": "Address",
   "AdvancedOptions": "高级选项",
   "Allows all requests to workloads in given namespace(s)": "Allows all requests to workloads in given namespace(s)",
   "An Istio Config resource needs at least one cluster selected": "An Istio Config resource needs at least one cluster selected",
@@ -119,7 +120,9 @@
   "Disable Auto Injection": "禁用自动注入",
   "Disabled": "禁用",
   "Display": "展示",
+  "Display envoy config for the selected pod": "Display envoy config for the selected pod",
   "Display the legend to learn about what the different shapes, colors and backgrounds mean.": "显示图例以了解不同形状、颜色和背景的含义。",
+  "Display ztunnel config for the selected pod": "Display ztunnel config for the selected pod",
   "Distributed Tracing": "分布式跟踪",
   "Done": "完成",
   "Download": "下载",
@@ -133,6 +136,7 @@
   "Empty value": "Empty value",
   "Enable Auto Injection": "启动自动注入",
   "Enabled": "启用",
+  "Endpoints": "Endpoints",
   "Enter a label in the format <label>=<value>. Enter one or multiple labels separated by comma.": "Enter a label in the format <label>=<value>. Enter one or multiple labels separated by comma.",
   "Error fetching Istio deployment status.": "Error fetching Istio deployment status.",
   "Evenly distribute traffic": "平均分配网络流量",
@@ -183,6 +187,7 @@
   "inbound": "入站",
   "Inbound": "入站",
   "Include mesh gateway": "包含mesh网关",
+  "Info": "Info",
   "Infra node": "Infra node",
   "Istio {{type}} created": "Istio {{type}} created",
   "Istio Ambient ztunnel detected in the Control plane": "在Istio的控制平面中检测到环境ztunnel",
@@ -259,11 +264,15 @@
   "No Route Rules defined": "未定义路由规则",
   "No Rules Defined.": "No Rules Defined.",
   "No Source Matches Defined": "No Source Matches Defined",
+  "No Ztunnel services found": "No Ztunnel services found",
+  "No Ztunnel workloads found": "No Ztunnel workloads found",
+  "Node": "Node",
   "Node Background": "Node Background",
   "Node Badges": "Node Badges",
   "Node Colors": "Node Colors",
   "Node Shapes": "Node Shapes",
   "none": "无",
+  "None": "None",
   "Normal": "Normal",
   "Normal Request": "Normal Request",
   "Not found": "Not found",
@@ -299,6 +308,7 @@
   "Percentage of requests on which the delay will be injected.": "注入了延迟的请求的占比。",
   "Percentage of requests to be aborted with the error code provided.": "中止并提供错误状态码的请求的比例。",
   "Please include this information when opening a bug:": "Please include this information when opening a bug:",
+  "Pod Name": "Pod Name",
   "Pod Status": "Pod Status",
   "Policy": "Policy",
   "Port": "端口",
@@ -307,6 +317,7 @@
   "Preview new istio objects": "Preview new istio objects",
   "Private Key": "私钥",
   "Private Key must be non empty": "私钥必须非空",
+  "Protocol": "Protocol",
   "Proxy sync time": "Proxy sync time",
   "Refresh interval": "Refresh interval",
   "Remove Auto Injection": "删除自动注入",
@@ -344,6 +355,8 @@
   "Select zoom area": "Select zoom area",
   "Service": "Service",
   "Service Entry": "Service Entry",
+  "Service Name": "Service Name",
+  "Service VIP": "Service VIP",
   "Services": "服务",
   "Set display options for the mesh.": "Set display options for the mesh.",
   "Set edge labeling, node badging, and various display options. Response-time edge labeling, security badging, and traffic animation may affect performance. Response-times reflect the 95th percentile.": "设置边标签、节点标签和各种显示选项。响应时间边缘标记、安全标记和流量动画可能会影响性能",
@@ -378,6 +391,7 @@
   "This rule is not accessible.": "该规则将无法生效。",
   "This shows the meshConfig.outboundTrafficPolicy.mode setting. It controls the sidecar handling of                 requests to external services (services not defined in Istio’s internal service registry). When                 set to ALLOW_ANY the Istio proxy performs a passthrough to the unknown services. When set to REGISTRY_ONLY                 the Istio proxy blocks requets to hosts without a defined HTTP service or service entry.": "This shows the meshConfig.outboundTrafficPolicy.mode setting. It controls the sidecar handling of                 requests to external services (services not defined in Istio’s internal service registry). When                 set to ALLOW_ANY the Istio proxy performs a passthrough to the unknown services. When set to REGISTRY_ONLY                 the Istio proxy blocks requets to hosts without a defined HTTP service or service entry.",
   "This type of workload is read-only": "This type of workload is read-only",
+  "This workload is an Istio Ambient waypoint proxy": "This workload is an Istio Ambient waypoint proxy",
   "Time duration": "Time duration",
   "Time range": "Time range",
   "Time Range": "Time Range",
@@ -428,6 +442,7 @@
   "Visit the Mesh page": "Visit the Mesh page",
   "Warn": "警告",
   "Waypoint": "Waypoint",
+  "Waypoint proxy": "Waypoint proxy",
   "Weighted routing": "权重路由",
   "When": "When",
   "When List": "When List",
@@ -436,5 +451,7 @@
   "Workload Selector": "Workload Selector",
   "Workloads": "工作负载",
   "Zoom": "Zoom",
-  "Zoom graph": "Zoom graph"
+  "Zoom graph": "Zoom graph",
+  "Ztunnel services config": "Ztunnel services config",
+  "Ztunnel workloads config": "Ztunnel workloads config"
 }
diff --git a/frontend/src/components/Ambient/WaypointLabel.tsx b/frontend/src/components/Ambient/WaypointLabel.tsx
new file mode 100644
index 000000000..ad38622d5
--- /dev/null
+++ b/frontend/src/components/Ambient/WaypointLabel.tsx
@@ -0,0 +1,38 @@
+import * as React from 'react';
+import { PFBadge, PFBadges } from '../Pf/PfBadges';
+import { Tooltip, TooltipPosition } from '@patternfly/react-core';
+import { KialiIcon } from '../../config/KialiIcon';
+import { infoStyle } from '../../styles/IconStyle';
+import { t } from 'i18next';
+
+export const renderWaypointLabel = (bgsize?: string): React.ReactNode => {
+  const badgeSize = bgsize === 'global' || bgsize === 'sm' ? bgsize : 'global';
+  return [
+    <>
+      <div key="waypoint-workloads-title">
+        <PFBadge badge={PFBadges.Waypoint} position={TooltipPosition.top} size={badgeSize} />
+        {t('Waypoint proxy')}
+        <Tooltip position={TooltipPosition.right} content={t('This workload is an Istio Ambient waypoint proxy')}>
+          <KialiIcon.Info className={infoStyle} />
+        </Tooltip>
+      </div>
+    </>
+  ];
+};
+
+export const renderWaypointSimpleLabel = (bgsize?: string): React.ReactNode => {
+  const badgeSize = bgsize === 'global' || bgsize === 'sm' ? bgsize : 'global';
+  const tooltip = <div>{t('This workload is an Istio Ambient waypoint proxy')}</div>;
+
+  return [
+    <>
+      <PFBadge
+        badge={PFBadges.Waypoint}
+        position={TooltipPosition.top}
+        size={badgeSize}
+        tooltip={tooltip}
+        style={{ marginLeft: '5px' }}
+      />
+    </>
+  ];
+};
diff --git a/frontend/src/components/Ambient/ZtunnelConfig.tsx b/frontend/src/components/Ambient/ZtunnelConfig.tsx
new file mode 100644
index 000000000..025a4159a
--- /dev/null
+++ b/frontend/src/components/Ambient/ZtunnelConfig.tsx
@@ -0,0 +1,233 @@
+import * as React from 'react';
+import { connect } from 'react-redux';
+import { KialiAppState } from 'store/Store';
+import { ISortBy } from '@patternfly/react-table';
+import { Workload } from 'types/Workload';
+import { Pod, ZtunnelConfigDump } from 'types/IstioObjects';
+import * as API from '../../services/Api';
+import * as AlertUtils from '../../utils/AlertUtils';
+import { Card, CardBody, Grid, GridItem, Tab, Tabs, TooltipPosition } from '@patternfly/react-core';
+import { activeTab } from '../../components/Tab/Tabs';
+import { RenderComponentScroll } from 'components/Nav/Page';
+import { location, router } from '../../app/History';
+import {
+  tabName as workloadTabName,
+  defaultTab as workloadDefaultTab
+} from '../../pages/WorkloadDetails/WorkloadDetailsPage';
+import { TimeInMilliseconds } from '../../types/Common';
+import { subTabStyle } from 'styles/TabStyles';
+import { ToolbarDropdown } from '../Dropdown/ToolbarDropdown';
+import { PFBadge, PFBadges } from '../Pf/PfBadges';
+import { kialiStyle } from '../../styles/StyleUtils';
+import { ZtunnelServicesTable } from './ZtunnelServicesTable';
+import { ZtunnelWorkloadsTable } from './ZtunnelWorkloadsTable';
+import { t } from 'i18next';
+import { SortableTh } from '../Table/SimpleTable';
+
+const resources: string[] = ['services', 'workloads'];
+
+const ztunnelTabs = ['services', 'workloads'];
+const tabName = 'ztunnelTab';
+const defaultTab = 'services';
+
+export type ResourceSorts = { [resource: string]: ISortBy };
+
+type ReduxProps = {
+  kiosk: string;
+};
+
+type ZtunnelConfigProps = ReduxProps & {
+  lastRefreshAt: TimeInMilliseconds;
+  namespace: string;
+  workload: Workload;
+};
+
+type ZtunnelConfigState = {
+  activeKey: number;
+  config: ZtunnelConfigDump;
+  fetch: boolean;
+  pod: Pod;
+  resource: string;
+  tabHeight: number;
+};
+
+const fullHeightStyle = kialiStyle({
+  height: '100%'
+});
+
+const iconStyle = kialiStyle({
+  display: 'inline-block',
+  alignSelf: 'center'
+});
+
+export interface SortableCompareTh<T> extends SortableTh {
+  compare?: (a: T, b: T) => number;
+}
+
+class ZtunnelConfigComponent extends React.Component<ZtunnelConfigProps, ZtunnelConfigState> {
+  constructor(props: ZtunnelConfigProps) {
+    super(props);
+
+    this.state = {
+      pod: this.sortedPods()[0],
+      config: {},
+      tabHeight: 300,
+      fetch: true,
+      activeKey: ztunnelTabs.indexOf(activeTab(tabName, defaultTab)),
+      resource: activeTab(tabName, defaultTab)
+    };
+  }
+
+  componentDidMount(): void {
+    this.fetchContent();
+  }
+
+  componentDidUpdate(_prevProps: ZtunnelConfigProps, prevState: ZtunnelConfigState): void {
+    const currentTabIndex = ztunnelTabs.indexOf(activeTab(tabName, defaultTab));
+
+    if (this.state.pod.name !== prevState.pod.name || this.state.resource !== prevState.resource) {
+      this.fetchContent();
+
+      if (currentTabIndex !== this.state.activeKey) {
+        this.setState({ activeKey: currentTabIndex });
+      }
+    }
+  }
+
+  ztunnelHandleTabClick = (_event: React.MouseEvent, tabIndex: string | number): void => {
+    const resourceIdx: number = +tabIndex;
+    const targetResource: string = resources[resourceIdx];
+
+    if (targetResource !== this.state.resource) {
+      this.setState({
+        config: {},
+        fetch: true,
+        resource: targetResource,
+        activeKey: resourceIdx
+      });
+
+      const mainTab = new URLSearchParams(location.getSearch()).get(workloadTabName) ?? workloadDefaultTab;
+      const urlParams = new URLSearchParams(location.getSearch());
+      urlParams.set(tabName, targetResource);
+      urlParams.set(workloadTabName, mainTab);
+      router.navigate(`${location.getPathname()}?${urlParams.toString()}`);
+    }
+  };
+
+  fetchZtunnelConfig = (): void => {
+    API.getPodZtunnelConfig(this.props.namespace, this.state.pod.name, this.props.workload.cluster)
+      .then(resultConfig => {
+        this.setState({
+          config: resultConfig.data,
+          fetch: false
+        });
+      })
+      .catch(error => {
+        AlertUtils.addError(`Could not fetch ztunnel config for ${this.state.pod.name}.`, error);
+      });
+  };
+
+  fetchContent = (): void => {
+    if (this.state.fetch === true) {
+      this.fetchZtunnelConfig();
+    }
+  };
+
+  setPod = (podName: string): void => {
+    const podIdx: number = +podName;
+    const targetPod: Pod = this.sortedPods()[podIdx];
+
+    if (targetPod.name !== this.state.pod.name) {
+      this.setState({
+        config: {},
+        pod: targetPod,
+        fetch: true
+      });
+    }
+  };
+
+  sortedPods = (): Pod[] => {
+    return this.props.workload.pods.sort((p1: Pod, p2: Pod) => (p1.name >= p2.name ? 1 : -1));
+  };
+
+  render(): React.ReactNode {
+    const tabs: JSX.Element[] = [];
+
+    const servicesTab = (
+      <Tab title={t('Services')} eventKey={0} key="services">
+        <Card className={fullHeightStyle}>
+          <CardBody>
+            <div className={fullHeightStyle}>
+              <div style={{ marginBottom: '1.25rem' }}>
+                <div key="service-icon" className={iconStyle}>
+                  <PFBadge badge={PFBadges.Pod} position={TooltipPosition.top} />
+                </div>
+                <ToolbarDropdown
+                  id="ztunnel_pods_list"
+                  tooltip={t('Display ztunnel config for the selected pod')}
+                  handleSelect={key => this.setPod(key)}
+                  value={this.state.pod.name}
+                  label={this.state.pod.name}
+                  options={this.props.workload.pods.map((pod: Pod) => pod.name).sort()}
+                />
+              </div>
+              <ZtunnelServicesTable config={this.state.config.services} />
+            </div>
+          </CardBody>
+        </Card>
+      </Tab>
+    );
+    tabs.push(servicesTab);
+
+    const workloadsTab = (
+      <Tab title={t('Workloads')} eventKey={1} key="workloads">
+        <Card className={fullHeightStyle}>
+          <CardBody>
+            <div className={fullHeightStyle}>
+              <div style={{ marginBottom: '1.25rem' }}>
+                <div key="service-icon" className={iconStyle}>
+                  <PFBadge badge={PFBadges.Pod} position={TooltipPosition.top} />
+                </div>
+                <ToolbarDropdown
+                  id="envoy_pods_list"
+                  tooltip={t('Display envoy config for the selected pod')}
+                  handleSelect={key => this.setPod(key)}
+                  value={this.state.pod.name}
+                  label={this.state.pod.name}
+                  options={this.props.workload.pods.map((pod: Pod) => pod.name).sort()}
+                />
+              </div>
+              <ZtunnelWorkloadsTable config={this.state.config.workloads} />
+            </div>
+          </CardBody>
+        </Card>
+      </Tab>
+    );
+    tabs.push(workloadsTab);
+
+    return (
+      <RenderComponentScroll onResize={height => this.setState({ tabHeight: height })}>
+        <Grid>
+          <GridItem span={12}>
+            <Tabs
+              id="ztunnel-details"
+              className={subTabStyle}
+              activeKey={this.state.activeKey}
+              onSelect={this.ztunnelHandleTabClick}
+              mountOnEnter={true}
+              unmountOnExit={true}
+            >
+              {tabs}
+            </Tabs>
+          </GridItem>
+        </Grid>
+      </RenderComponentScroll>
+    );
+  }
+}
+
+const mapStateToProps = (state: KialiAppState): ReduxProps => ({
+  kiosk: state.globalState.kiosk
+});
+
+export const ZtunnelConfig = connect(mapStateToProps)(ZtunnelConfigComponent);
diff --git a/frontend/src/components/Ambient/ZtunnelServicesTable.tsx b/frontend/src/components/Ambient/ZtunnelServicesTable.tsx
new file mode 100644
index 000000000..8fd37f249
--- /dev/null
+++ b/frontend/src/components/Ambient/ZtunnelServicesTable.tsx
@@ -0,0 +1,114 @@
+import * as React from 'react';
+import { IRow, ISortBy, OnSort, SortByDirection, TableVariant } from '@patternfly/react-table';
+import { ZtunnelEndpoint, ZtunnelService } from '../../types/IstioObjects';
+import { SimpleTable } from '../Table/SimpleTable';
+import { EmptyState, EmptyStateBody, EmptyStateVariant } from '@patternfly/react-core';
+import { kialiStyle } from '../../styles/StyleUtils';
+import { t } from 'i18next';
+import { SortableCompareTh } from './ZtunnelConfig';
+
+type ZtunnelServicesProps = {
+  config?: ZtunnelService[];
+};
+
+export const emtpytStyle = kialiStyle({
+  padding: 0,
+  margin: 0
+});
+
+const columns: SortableCompareTh<ZtunnelService>[] = [
+  {
+    title: t('Namespace'),
+    sortable: true,
+    compare: (a, b) => a.namespace.localeCompare(b.namespace)
+  },
+  {
+    title: t('Service Name'),
+    sortable: true,
+    compare: (a, b) => a.name.localeCompare(b.name)
+  },
+  {
+    title: t('Service VIP'),
+    sortable: false
+  },
+  {
+    title: t('Waypoint'),
+    sortable: true,
+    compare: (a, b) => a.waypoint.destination.localeCompare(b.waypoint.destination)
+  },
+  {
+    title: t('Endpoints'),
+    sortable: false
+  }
+];
+
+export const ZtunnelServicesTable: React.FC<ZtunnelServicesProps> = (props: ZtunnelServicesProps) => {
+  const [sortBy, setSortBy] = React.useState(0);
+  const [sortDirection, setSortDirection] = React.useState(SortByDirection.asc);
+
+  const getEndpoints = (endpoints: Record<string, ZtunnelEndpoint>): string => {
+    let total = 0;
+    let up = 0;
+    Object.entries(endpoints).forEach(([_, endpoint]) => {
+      total++;
+      if (endpoint.status === 'Healthy') {
+        up++;
+      }
+      return;
+    });
+    return `${total}/${up}`;
+  };
+
+  const compare = columns[sortBy].compare;
+  const sorted = compare
+    ? props.config?.sort(sortDirection === SortByDirection.asc ? compare : (a, b) => compare(b, a))
+    : props.config;
+
+  const rows: IRow[] = sorted
+    ? sorted.map(service => {
+        return {
+          cells: [
+            service.namespace,
+            service.name,
+            service.vips.map(sb => {
+              return sb;
+            }),
+            service.waypoint.destination ? (
+              <>
+                {service.waypoint.destination}:{service.waypoint.hboneMtlsPort}
+              </>
+            ) : (
+              t('None')
+            ),
+            getEndpoints(service.endpoints)
+          ]
+        };
+      })
+    : [];
+
+  const sort: ISortBy = { index: sortBy, direction: sortDirection };
+  const onSort: OnSort = (_event: React.MouseEvent, index: number, sortDirection: SortByDirection) => {
+    setSortBy(index);
+    setSortDirection(sortDirection);
+  };
+
+  const noServicesConfig: React.ReactNode = (
+    <EmptyState variant={EmptyStateVariant.sm} className={emtpytStyle}>
+      <EmptyStateBody className={emtpytStyle} data-test="istio-config-empty">
+        {t('No Ztunnel services found')}
+      </EmptyStateBody>
+    </EmptyState>
+  );
+
+  return (
+    <SimpleTable
+      label={t('Ztunnel services config')}
+      columns={columns}
+      rows={rows}
+      variant={TableVariant.compact}
+      emptyState={noServicesConfig}
+      sortBy={sort}
+      onSort={onSort}
+    />
+  );
+};
diff --git a/frontend/src/components/Ambient/ZtunnelWorkloadsTable.tsx b/frontend/src/components/Ambient/ZtunnelWorkloadsTable.tsx
new file mode 100644
index 000000000..bf2b0f500
--- /dev/null
+++ b/frontend/src/components/Ambient/ZtunnelWorkloadsTable.tsx
@@ -0,0 +1,101 @@
+import * as React from 'react';
+import { IRow, ISortBy, OnSort, SortByDirection, TableVariant } from '@patternfly/react-table';
+import { ZtunnelWorkload } from '../../types/IstioObjects';
+import { EmptyState, EmptyStateBody, EmptyStateVariant } from '@patternfly/react-core';
+import { emtpytStyle } from './ZtunnelServicesTable';
+import { SimpleTable } from '../Table/SimpleTable';
+import { t } from 'i18next';
+import { SortableCompareTh } from './ZtunnelConfig';
+
+type ZtunnelWorkloadsProps = {
+  config?: ZtunnelWorkload[];
+};
+
+const columns: SortableCompareTh<ZtunnelWorkload>[] = [
+  {
+    title: t('Namespace'),
+    sortable: true,
+    compare: (a, b) => a.namespace.localeCompare(b.namespace)
+  },
+  {
+    title: t('Pod Name'),
+    sortable: true,
+    compare: (a, b) => a.name.localeCompare(b.name)
+  },
+  {
+    title: t('Address'),
+    sortable: false
+  },
+  {
+    title: t('Node'),
+    sortable: true,
+    compare: (a, b) => a.node.localeCompare(b.node)
+  },
+  {
+    title: t('Waypoint'),
+    sortable: false
+  },
+  {
+    title: t('Protocol'),
+    sortable: false
+  }
+];
+
+export const ZtunnelWorkloadsTable: React.FC<ZtunnelWorkloadsProps> = (props: ZtunnelWorkloadsProps) => {
+  const [sortBy, setSortBy] = React.useState(0);
+  const [sortDirection, setSortDirection] = React.useState(SortByDirection.asc);
+
+  const compare = columns[sortBy].compare;
+  const sorted = compare
+    ? props.config?.sort(sortDirection === SortByDirection.asc ? compare : (a, b) => compare(b, a))
+    : props.config;
+
+  const sort: ISortBy = { index: sortBy, direction: sortDirection };
+  const onSort: OnSort = (_event: React.MouseEvent, index: number, sortDirection: SortByDirection) => {
+    setSortBy(index);
+    setSortDirection(sortDirection);
+  };
+
+  const rows: IRow[] = sorted
+    ? sorted.map(workload => {
+        return {
+          cells: [
+            workload.namespace,
+            workload.name,
+            workload.workloadIps.map(ip => {
+              return ip;
+            }),
+            workload.node,
+            workload.waypoint ? (
+              <>
+                {workload.waypoint.destination}:{workload.waypoint.hboneMtlsPort}
+              </>
+            ) : (
+              t('None')
+            ),
+            workload.protocol
+          ]
+        };
+      })
+    : [];
+
+  const noWorkloadsConfig: React.ReactNode = (
+    <EmptyState variant={EmptyStateVariant.sm} className={emtpytStyle}>
+      <EmptyStateBody className={emtpytStyle} data-test="istio-config-empty">
+        {t('No Ztunnel workloads found')}
+      </EmptyStateBody>
+    </EmptyState>
+  );
+
+  return (
+    <SimpleTable
+      label={t('Ztunnel workloads config')}
+      columns={columns}
+      rows={rows}
+      variant={TableVariant.compact}
+      emptyState={noWorkloadsConfig}
+      sortBy={sort}
+      onSort={onSort}
+    />
+  );
+};
diff --git a/frontend/src/components/DetailDescription/DetailDescription.tsx b/frontend/src/components/DetailDescription/DetailDescription.tsx
index ffe09f9c8..379be3ac0 100644
--- a/frontend/src/components/DetailDescription/DetailDescription.tsx
+++ b/frontend/src/components/DetailDescription/DetailDescription.tsx
@@ -26,6 +26,7 @@ type Props = ReduxProps & {
   apps?: string[];
   cluster?: string;
   health?: H.Health;
+  isWaypoint?: boolean;
   namespace: string;
   services?: string[];
   waypointWorkloads?: Workload[];
@@ -56,25 +57,44 @@ const itemStyle = kialiStyle({
   paddingBottom: '0.25rem'
 });
 
-export const renderWaypoint = (bgsize?: string): React.ReactNode => {
-  const badgeSize = bgsize === 'global' || bgsize === 'sm' ? bgsize : 'global';
-  return [
-    <>
-      <div key="waypoint-workloads-title">
-        <PFBadge badge={PFBadges.Waypoint} position={TooltipPosition.top} size={badgeSize} />
-        Waypoint proxy
-        <Tooltip
-          position={TooltipPosition.right}
-          content="This workload is identified as a waypoint proxy, as part of Istio Ambient"
+const DetailDescriptionComponent: React.FC<Props> = (props: Props) => {
+  const renderWaypoints = (): React.ReactNode => {
+    const waypointList = props.waypointWorkloads?.map(waypoint => {
+      let href = `/namespaces/${waypoint.namespace}/workloads/${waypoint.name}`;
+      if (props.cluster && isMultiCluster) {
+        href = `${href}?clusterName=${props.cluster}`;
+      }
+      const link = isParentKiosk(props.kiosk) ? (
+        <Link
+          to=""
+          onClick={() => {
+            kioskContextMenuAction(href);
+          }}
         >
-          <KialiIcon.Info className={infoStyle} />
-        </Tooltip>
+          {waypoint.name}
+        </Link>
+      ) : (
+        <Link to={href}>{waypoint.name}</Link>
+      );
+      return (
+        <li key={`App_${waypoint.namespace}_${waypoint.name}`} className={itemStyle}>
+          <div className={iconStyle}>
+            <PFBadge badge={PFBadges.Waypoint} position={TooltipPosition.top} />
+          </div>
+
+          <span>{link}</span>
+        </li>
+      );
+    });
+    return [
+      <div key="waypoint-list" className={resourceListStyle}>
+        <ul id="waypoint-list" style={{ listStyleType: 'none' }}>
+          {waypointList}
+        </ul>
       </div>
-    </>
-  ];
-};
+    ];
+  };
 
-const DetailDescriptionComponent: React.FC<Props> = (props: Props) => {
   const renderAppItem = (namespace: string, appName: string): React.ReactNode => {
     let href = `/namespaces/${namespace}/applications/${appName}`;
 
@@ -366,7 +386,7 @@ const DetailDescriptionComponent: React.FC<Props> = (props: Props) => {
       {props.workloads !== undefined && workloadSummary()}
       {props.services !== undefined && serviceList()}
       {props.health && renderTrafficStatus(props.health)}
-      {props.waypointWorkloads && renderWaypoint()}
+      {props.waypointWorkloads && renderWaypoints()}
     </div>
   );
 };
diff --git a/frontend/src/components/Time/TimeDurationModal.tsx b/frontend/src/components/Time/TimeDurationModal.tsx
index 4c162af13..47311d7eb 100644
--- a/frontend/src/components/Time/TimeDurationModal.tsx
+++ b/frontend/src/components/Time/TimeDurationModal.tsx
@@ -1,7 +1,7 @@
 import * as React from 'react';
 import { Button, Form, FormGroup, Modal, ModalVariant, TooltipPosition } from '@patternfly/react-core';
 import { UserSettingsActions } from '../../actions/UserSettingsActions';
-import { HistoryManager, URLParam } from '../../app/History';
+import { HistoryManager, location, URLParam } from '../../app/History';
 import { useKialiDispatch, useKialiSelector } from '../../hooks/redux';
 import { DurationInSeconds, IntervalInMilliseconds, TimeRange } from '../../types/Common';
 import { DurationDropdownComponent } from '../Dropdown/DurationDropdown';
@@ -9,6 +9,8 @@ import { RefreshComponent } from '../Refresh/Refresh';
 import { TimeRangeComp } from './TimeRangeComponent';
 import { kioskDurationAction, kioskTimeRangeAction } from '../Kiosk/KioskActions';
 import { useKialiTranslation } from 'utils/I18nUtils';
+import { toValidDuration } from '../../config/ServerConfig';
+import { config } from '../../config';
 
 interface Props {
   customDuration: boolean;
@@ -26,9 +28,69 @@ export const TimeDurationModal: React.FC<Props> = (props: Props) => {
   const reduxRefreshInterval = useKialiSelector(state => state.userSettings.refreshInterval);
   const reduxTimeRange = useKialiSelector(state => state.userSettings.timeRange);
 
-  const [duration, setDuration] = React.useState(reduxDuration);
-  const [refreshInterval, setRefreshInterval] = React.useState(reduxRefreshInterval);
-  const [timeRange, setTimeRange] = React.useState(reduxTimeRange);
+  const [duration, setDuration] = React.useState<number>(0);
+  const [refreshInterval, setRefreshInterval] = React.useState<number>(0);
+  const [timeRange, setTimeRange] = React.useState<TimeRange>({});
+
+  React.useEffect(() => {
+    const urlParams = new URLSearchParams(location.getSearch());
+    const urlDuration = HistoryManager.getDuration(urlParams);
+    const urlRefresh = HistoryManager.getNumericParam(URLParam.REFRESH_INTERVAL, urlParams);
+    const urlTimeRange = HistoryManager.getNumericParam(URLParam.RANGE_DURATION, urlParams);
+    const urlFrom = HistoryManager.getNumericParam(URLParam.FROM, urlParams);
+    const urlTo = HistoryManager.getNumericParam(URLParam.TO, urlParams);
+
+    const getInitDuration = (): number => {
+      if (!props.customDuration && urlDuration) {
+        return toValidDuration(urlDuration);
+      }
+      return reduxDuration;
+    };
+
+    const getInitRefresh = (): number => {
+      if (urlRefresh) {
+        // Validate value
+        if (urlRefresh === 0 || config.toolbar.refreshInterval[urlRefresh]) {
+          return urlRefresh;
+        }
+      }
+      return reduxRefreshInterval;
+    };
+
+    const getUrlTimeRange = (): TimeRange => ({
+      ...(urlTimeRange != null && { rangeDuration: urlTimeRange }),
+      ...(urlFrom != null && { from: urlFrom }),
+      ...(urlTo != null && { to: urlTo })
+    });
+
+    const getInitTimeRange = (): TimeRange => {
+      const tm = getUrlTimeRange();
+
+      if (!tm.rangeDuration && !tm.from && !tm.to && props.customDuration) {
+        return reduxTimeRange;
+      }
+
+      return tm;
+    };
+
+    setDuration(getInitDuration());
+    setRefreshInterval(getInitRefresh());
+    setTimeRange(getInitTimeRange());
+
+    if (urlDuration !== undefined) {
+      dispatch(UserSettingsActions.setDuration(urlDuration));
+    }
+
+    // Update just when valid
+    if (urlRefresh !== undefined && (urlRefresh === 0 || config.toolbar.refreshInterval[urlRefresh])) {
+      dispatch(UserSettingsActions.setRefreshInterval(urlRefresh));
+    }
+
+    if (getUrlTimeRange() !== undefined) {
+      dispatch(UserSettingsActions.setTimeRange(getUrlTimeRange()));
+    }
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, []);
 
   const handleCancel = (): void => {
     // reset the dialog
@@ -44,11 +106,23 @@ export const TimeDurationModal: React.FC<Props> = (props: Props) => {
 
   const handleConfirm = (): void => {
     dispatch(UserSettingsActions.setRefreshInterval(refreshInterval));
+    HistoryManager.setParam(URLParam.REFRESH_INTERVAL, String(refreshInterval));
 
     if (!props.customDuration) {
       dispatch(UserSettingsActions.setDuration(duration));
+      HistoryManager.setParam(URLParam.DURATION, String(duration));
       kioskDurationAction(duration);
     } else {
+      timeRange.rangeDuration
+        ? HistoryManager.setParam(URLParam.RANGE_DURATION, String(timeRange.rangeDuration))
+        : HistoryManager.deleteParam(URLParam.RANGE_DURATION);
+      timeRange.from
+        ? HistoryManager.setParam(URLParam.FROM, String(timeRange.from))
+        : HistoryManager.deleteParam(URLParam.FROM);
+      timeRange.to
+        ? HistoryManager.setParam(URLParam.TO, String(timeRange.to))
+        : HistoryManager.deleteParam(URLParam.TO);
+
       dispatch(UserSettingsActions.setTimeRange(timeRange));
       kioskTimeRangeAction(timeRange);
     }
diff --git a/frontend/src/components/VirtualList/Config.ts b/frontend/src/components/VirtualList/Config.ts
index cc20e678b..7047003d2 100644
--- a/frontend/src/components/VirtualList/Config.ts
+++ b/frontend/src/components/VirtualList/Config.ts
@@ -1,13 +1,12 @@
 import deepFreeze from 'deep-freeze';
 import { AppListItem } from '../../types/AppList';
 import { AppWorkload } from 'types/App';
-import { isWaypoint } from 'helpers/LabelFilterHelper';
 import { WorkloadListItem, Workload } from '../../types/Workload';
 import { ServiceListItem } from '../../types/ServiceList';
 import { dicTypeToGVK, IstioConfigItem } from '../../types/IstioConfigList';
 import * as Renderers from './Renderers';
 import { Health } from '../../types/Health';
-import { isIstioNamespace } from 'config/ServerConfig';
+import { isIstioNamespace, serverConfig } from 'config/ServerConfig';
 import { NamespaceInfo } from '../../types/NamespaceInfo';
 import { StatefulFiltersRef } from '../Filters/StatefulFilters';
 import { PFBadges, PFBadgeType } from '../../components/Pf/PfBadges';
@@ -31,14 +30,16 @@ export const hasHealth = (r: RenderResource): r is SortResource => {
 
 export const hasMissingSidecar = (workload: Workload | WorkloadListItem | AppWorkload | AppListItem): boolean => {
   return (
-    // TODO:
-    // Are we missing an ambient case here where ambient is enabled AND we are missing the ambient labels AND we are missing the sidecar?
-    // hasMissingSC && hasMissingA && serverConfig.ambientEnabled
-    !workload.istioSidecar &&
-    !workload.isAmbient &&
-    !isWaypoint(workload.labels) &&
-    !workload.isGateway &&
-    !isIstioNamespace(workload.namespace)
+    (!serverConfig.ambientEnabled &&
+      !workload.istioSidecar &&
+      !workload.isGateway &&
+      !isIstioNamespace(workload.namespace)) ||
+    (serverConfig.ambientEnabled &&
+      !workload.isAmbient &&
+      !workload.istioSidecar &&
+      workload.ambient !== 'waypoint' &&
+      !workload.isGateway &&
+      !isIstioNamespace(workload.namespace))
   );
 };
 
diff --git a/frontend/src/components/VirtualList/Renderers.tsx b/frontend/src/components/VirtualList/Renderers.tsx
index b76819be3..379905fe0 100644
--- a/frontend/src/components/VirtualList/Renderers.tsx
+++ b/frontend/src/components/VirtualList/Renderers.tsx
@@ -39,7 +39,6 @@ import { Label } from 'components/Label/Label';
 import { isMultiCluster, serverConfig } from 'config/ServerConfig';
 import { ControlPlaneBadge } from 'pages/Overview/ControlPlaneBadge';
 import { NamespaceStatuses } from 'pages/Overview/NamespaceStatuses';
-import { isWaypoint } from '../../helpers/LabelFilterHelper';
 import { KialiIcon } from '../../config/KialiIcon';
 import { Td } from '@patternfly/react-table';
 import { kialiStyle } from 'styles/StyleUtils';
@@ -100,9 +99,9 @@ export const details: Renderer<AppListItem | WorkloadListItem | ServiceListItem>
   item: AppListItem | WorkloadListItem | ServiceListItem
 ) => {
   const isWorkload = item.instanceType === InstanceType.Workload;
-  const isAmbientWaypoint = isWaypoint(item.labels);
-  const hasMissingApp = isWorkload && !item['appLabel'] && !isWaypoint(item.labels);
-  const hasMissingVersion = isWorkload && !item['versionLabel'] && !isWaypoint(item.labels);
+  const isAmbientWaypoint = item.ambient === 'waypoint';
+  const hasMissingApp = isWorkload && !item['appLabel'] && item.ambient !== 'waypoint';
+  const hasMissingVersion = isWorkload && !item['versionLabel'] && item.ambient !== 'waypoint';
   const additionalDetails = (item as WorkloadListItem | ServiceListItem).additionalDetailSample;
   const spacer = isWorkload && hasMissingSidecar(item) && additionalDetails && additionalDetails.icon;
   const hasMissingAP = isWorkload && (item as WorkloadListItem).notCoveredAuthPolicy;
diff --git a/frontend/src/config/Config.ts b/frontend/src/config/Config.ts
index f3a9b4e2a..a9a7c2767 100644
--- a/frontend/src/config/Config.ts
+++ b/frontend/src/config/Config.ts
@@ -165,6 +165,8 @@ const conf = {
       podEnvoyProxyLogging: (namespace: string, pod: string) => `api/namespaces/${namespace}/pods/${pod}/logging`,
       podEnvoyProxyResourceEntries: (namespace: string, pod: string, resource: string) =>
         `api/namespaces/${namespace}/pods/${pod}/config_dump/${resource}`,
+      podZtunnelConfig: (namespace: string, pod: string) =>
+        `api/namespaces/${namespace}/pods/${pod}/config_dump_ztunnel`,
       serverConfig: `api/config`,
       service: (namespace: string, service: string) => `api/namespaces/${namespace}/services/${service}`,
       serviceGraphElements: (namespace: string, service: string) =>
diff --git a/frontend/src/helpers/LabelFilterHelper.ts b/frontend/src/helpers/LabelFilterHelper.ts
index 8a2bdc5fe..6803d2909 100644
--- a/frontend/src/helpers/LabelFilterHelper.ts
+++ b/frontend/src/helpers/LabelFilterHelper.ts
@@ -1,7 +1,6 @@
 import { AppListItem } from '../types/AppList';
 import { WorkloadListItem } from '../types/Workload';
 import { ServiceListItem } from '../types/ServiceList';
-import { serverConfig } from '../config';
 
 type itemsType = AppListItem | ServiceListItem | WorkloadListItem;
 
@@ -99,11 +98,3 @@ const getKeyAndValues = (filters: string[]): { keyValues: string[]; keys: string
   const keyValues = filters.filter(f => f.includes('='));
   return { keyValues, keys };
 };
-
-export const isWaypoint = (labels: { [key: string]: string }): boolean => {
-  return (
-    labels &&
-    serverConfig.istioLabels.ambientWaypointLabel in labels &&
-    labels[serverConfig.istioLabels.ambientWaypointLabel] === serverConfig.istioLabels.ambientWaypointLabelValue
-  );
-};
diff --git a/frontend/src/pages/Graph/SummaryPanelNode.tsx b/frontend/src/pages/Graph/SummaryPanelNode.tsx
index 3f9d936ad..ceb538e3e 100644
--- a/frontend/src/pages/Graph/SummaryPanelNode.tsx
+++ b/frontend/src/pages/Graph/SummaryPanelNode.tsx
@@ -39,8 +39,8 @@ import { useKialiSelector } from '../../hooks/redux';
 import { groupMenuStyle, kebabToggleStyle } from 'styles/DropdownStyles';
 import { isMultiCluster, serverConfig } from '../../config';
 import { panelBodyStyle, panelHeadingStyle, panelStyle } from './SummaryPanelStyle';
-import { renderWaypoint } from '../../components/DetailDescription/DetailDescription';
 import { dicTypeToGVK, gvkType } from '../../types/IstioConfigList';
+import { renderWaypointLabel } from '../../components/Ambient/WaypointLabel';
 
 type SummaryPanelNodeState = {
   isActionOpen: boolean;
@@ -238,7 +238,7 @@ export class SummaryPanelNodeComponent extends React.Component<SummaryPanelNodeC
 
           <div>
             {this.renderBadgeSummary(nodeData)}
-            {nodeData.isWaypoint && renderWaypoint('sm')}
+            {nodeData.isWaypoint && renderWaypointLabel('sm')}
             {shouldRenderDestsList && <div>{destsList}</div>}
             {shouldRenderSvcList && <div>{servicesList}</div>}
             {shouldRenderService && <div>{renderBadgedLink(nodeData, NodeType.SERVICE)}</div>}
diff --git a/frontend/src/pages/ServiceDetails/ServiceDescription.tsx b/frontend/src/pages/ServiceDetails/ServiceDescription.tsx
index 017547b8a..41e2d689a 100644
--- a/frontend/src/pages/ServiceDetails/ServiceDescription.tsx
+++ b/frontend/src/pages/ServiceDetails/ServiceDescription.tsx
@@ -68,6 +68,7 @@ export const ServiceDescription: React.FC<ServiceInfoDescriptionProps> = (props:
           }
 
           workloads.push({
+            ambient: wk.ambient,
             namespace: wk.namespace,
             workloadName: wk.name,
             istioSidecar: wk.istioSidecar,
diff --git a/frontend/src/pages/ServiceDetails/ServiceNetwork.tsx b/frontend/src/pages/ServiceDetails/ServiceNetwork.tsx
index 2604e0a0a..1e41ce87e 100644
--- a/frontend/src/pages/ServiceDetails/ServiceNetwork.tsx
+++ b/frontend/src/pages/ServiceDetails/ServiceNetwork.tsx
@@ -77,6 +77,8 @@ export const ServiceNetwork: React.FC<ServiceNetworkProps> = (props: ServiceNetw
     return hostnames;
   };
 
+  const service = props.serviceDetails.service;
+  let ips = service.type !== 'External' ? (service.ips ? service.ips : [service.ip]) : [];
   return (
     <Card isCompact={true} id="ServiceNetworkCard">
       <CardHeader>
@@ -89,21 +91,19 @@ export const ServiceNetwork: React.FC<ServiceNetworkProps> = (props: ServiceNetw
           <ul style={{ listStyleType: 'none' }}>
             <li>
               <span>Type</span>
-              {props.serviceDetails.service.type}
+              {service.type}
             </li>
 
-            {props.serviceDetails.service.type !== 'External' && (
+            {ips.map((ip, i) => (
               <li>
-                <span>{props.serviceDetails.service.type !== 'ExternalName' ? 'Service IP' : 'ExternalName'}</span>
-                {props.serviceDetails.service.type !== 'ExternalName'
-                  ? props.serviceDetails.service.ip
-                    ? props.serviceDetails.service.ip
-                    : ''
-                  : props.serviceDetails.service.externalName
-                  ? props.serviceDetails.service.externalName
-                  : ''}
+                <span>
+                  {service.type !== 'ExternalName'
+                    ? `Service ${service.ipFamilies ? service.ipFamilies[i] : 'IP'}`
+                    : 'ExternalName'}
+                </span>
+                {service.type !== 'ExternalName' ? ip : service.externalName ? service.externalName : ''}
               </li>
-            )}
+            ))}
 
             {props.serviceDetails.endpoints && props.serviceDetails.endpoints.length > 0 && (
               <li>
@@ -135,11 +135,11 @@ export const ServiceNetwork: React.FC<ServiceNetworkProps> = (props: ServiceNetw
               </li>
             )}
 
-            {props.serviceDetails.service.ports && props.serviceDetails.service.ports.length > 0 && (
+            {service.ports && service.ports.length > 0 && (
               <li>
                 <span>Ports</span>
                 <div style={{ display: 'inline-block' }}>
-                  {(props.serviceDetails.service.ports ?? []).map((port, i) => {
+                  {(service.ports ?? []).map((port, i) => {
                     return (
                       <div key={`port_${i}`}>
                         <div>
diff --git a/frontend/src/pages/WorkloadDetails/PodStatus.tsx b/frontend/src/pages/WorkloadDetails/PodStatus.tsx
index 14edfb2b4..69baf2637 100644
--- a/frontend/src/pages/WorkloadDetails/PodStatus.tsx
+++ b/frontend/src/pages/WorkloadDetails/PodStatus.tsx
@@ -1,6 +1,15 @@
 import * as React from 'react';
 import { ObjectCheck, ValidationTypes } from '../../types/IstioObjects';
-import { DEGRADED, HEALTHY, isProxyStatusSynced, mergeStatus, ProxyStatus, Status } from '../../types/Health';
+import {
+  DEGRADED,
+  hasProxyStatusInfoSeverity,
+  HEALTHY,
+  INFO,
+  isProxyStatusSynced,
+  mergeStatus,
+  ProxyStatus,
+  Status
+} from '../../types/Health';
 import { Tooltip, TooltipPosition } from '@patternfly/react-core';
 import { ProxyStatusList } from './ProxyStatusList';
 import { highestSeverity, validationToHealth } from '../../types/ServiceInfo';
@@ -13,7 +22,12 @@ type PodStatusProps = {
 };
 
 export const PodStatus: React.FC<PodStatusProps> = (props: PodStatusProps) => {
-  const proxyStatusSeverity: Status = props.proxyStatus && !isProxyStatusSynced(props.proxyStatus) ? DEGRADED : HEALTHY;
+  const proxyStatusSeverity: Status =
+    props.proxyStatus && hasProxyStatusInfoSeverity(props.proxyStatus)
+      ? INFO
+      : props.proxyStatus && !isProxyStatusSynced(props.proxyStatus)
+      ? DEGRADED
+      : HEALTHY;
 
   const showTooltip = (): boolean => {
     const validationSeverity: ValidationTypes = highestSeverity(props.checks || []);
diff --git a/frontend/src/pages/WorkloadDetails/WorkloadDescription.tsx b/frontend/src/pages/WorkloadDetails/WorkloadDescription.tsx
index 903ad4ecf..c7a04307e 100644
--- a/frontend/src/pages/WorkloadDetails/WorkloadDescription.tsx
+++ b/frontend/src/pages/WorkloadDetails/WorkloadDescription.tsx
@@ -17,12 +17,12 @@ import { MissingLabel } from '../../components/MissingLabel/MissingLabel';
 import { MissingAuthPolicy } from 'components/MissingAuthPolicy/MissingAuthPolicy';
 import { getGVKTypeString, hasMissingAuthPolicy, isGVKSupported } from 'utils/IstioConfigUtils';
 import { DetailDescription } from '../../components/DetailDescription/DetailDescription';
-import { isWaypoint } from '../../helpers/LabelFilterHelper';
 import { AmbientLabel, tooltipMsgType } from '../../components/Ambient/AmbientLabel';
 import { gvkType, validationKey } from '../../types/IstioConfigList';
 import { infoStyle } from 'styles/IconStyle';
 import { addInfo } from 'utils/AlertUtils';
 import { classes } from 'typestyle';
+import { renderWaypointSimpleLabel } from '../../components/Ambient/WaypointLabel';
 
 type WorkloadDescriptionProps = {
   health?: H.Health;
@@ -197,7 +197,7 @@ export const WorkloadDescription: React.FC<WorkloadDescriptionProps> = (props: W
             />
           )}
 
-          {workload.isAmbient && !isWaypoint(workload.labels) && (
+          {workload.isAmbient && workload.ambient !== 'waypoint' && (
             <AmbientLabel
               tooltip={tooltipMsgType.workload}
               waypoint={workload.waypointWorkloads?.length > 0 ? true : false}
@@ -213,7 +213,7 @@ export const WorkloadDescription: React.FC<WorkloadDescriptionProps> = (props: W
             />
           )}
 
-          {(!workload.appLabel || !workload.versionLabel) && !isWaypoint(workload.labels) && (
+          {(!workload.appLabel || !workload.versionLabel) && workload.ambient !== 'waypoint' && (
             <MissingLabel
               missingApp={!workload.appLabel}
               missingVersion={!workload.versionLabel}
@@ -221,6 +221,8 @@ export const WorkloadDescription: React.FC<WorkloadDescriptionProps> = (props: W
               tooltip={true}
             />
           )}
+
+          {workload.ambient === 'waypoint' && renderWaypointSimpleLabel()}
         </Title>
 
         {workload.cluster && isMultiCluster && (
@@ -244,7 +246,8 @@ export const WorkloadDescription: React.FC<WorkloadDescriptionProps> = (props: W
           services={services}
           health={props.health}
           cluster={props.workload?.cluster}
-          waypointWorkloads={isWaypoint(workload.labels) ? workload.waypointWorkloads : undefined}
+          isWaypoint={workload.ambient === 'waypoint'}
+          waypointWorkloads={workload.ambient !== 'waypoint' ? workload.waypointWorkloads : []}
         />
       </CardBody>
     </Card>
diff --git a/frontend/src/pages/WorkloadDetails/WorkloadDetailsPage.tsx b/frontend/src/pages/WorkloadDetails/WorkloadDetailsPage.tsx
index aba1d4e90..1363e2715 100644
--- a/frontend/src/pages/WorkloadDetails/WorkloadDetailsPage.tsx
+++ b/frontend/src/pages/WorkloadDetails/WorkloadDetailsPage.tsx
@@ -25,9 +25,9 @@ import { RenderHeader } from '../../components/Nav/Page/RenderHeader';
 import { ErrorSection } from '../../components/ErrorSection/ErrorSection';
 import { ErrorMsg } from '../../types/ErrorMsg';
 import { connectRefresh } from '../../components/Refresh/connectRefresh';
-import { isWaypoint } from '../../helpers/LabelFilterHelper';
 import { HistoryManager } from 'app/History';
 import { basicTabStyle } from 'styles/TabStyles';
+import { ZtunnelConfig } from '../../components/Ambient/ZtunnelConfig';
 
 type WorkloadDetailsState = {
   cluster?: string;
@@ -85,7 +85,7 @@ class WorkloadDetailsPageComponent extends React.Component<WorkloadDetailsPagePr
       currentTab !== this.state.currentTab ||
       this.props.duration !== prevProps.duration
     ) {
-      if (currentTab === 'info' || currentTab === 'logs' || currentTab === 'envoy') {
+      if (currentTab === 'info' || currentTab === 'logs' || currentTab === 'envoy' || currentTab === 'ztunnel') {
         this.fetchWorkload(cluster).then(() => {
           if (currentTab !== this.state.currentTab || cluster !== this.state.cluster) {
             this.setState({ currentTab: currentTab, cluster: cluster });
@@ -241,7 +241,10 @@ class WorkloadDetailsPageComponent extends React.Component<WorkloadDetailsPagePr
       );
     }
 
-    if (this.state.workload && this.hasIstioSidecars(this.state.workload) && !isWaypoint(this.state.workload.labels)) {
+    if (
+      this.state.workload &&
+      (this.hasIstioSidecars(this.state.workload) || this.state.workload.ambient === 'waypoint')
+    ) {
       const envoyTab = (
         <Tab title="Envoy" eventKey={10} key="Envoy">
           {this.state.workload && (
@@ -257,6 +260,22 @@ class WorkloadDetailsPageComponent extends React.Component<WorkloadDetailsPagePr
       paramToTab['envoy'] = 10;
     }
 
+    if (this.state.workload && this.state.workload.ambient === 'ztunnel') {
+      const ztunnelTab = (
+        <Tab title="Ztunnel" eventKey={11} key="Ztunnel">
+          {this.state.workload && (
+            <ZtunnelConfig
+              lastRefreshAt={this.props.lastRefreshAt}
+              namespace={this.props.workloadId.namespace}
+              workload={this.state.workload}
+            />
+          )}
+        </Tab>
+      );
+      tabsArray.push(ztunnelTab);
+      paramToTab['ztunnel'] = 11;
+    }
+
     // Used by the runtimes tabs
     nextTabIndex = tabsArray.length + 1;
 
@@ -273,8 +292,11 @@ class WorkloadDetailsPageComponent extends React.Component<WorkloadDetailsPagePr
         } else if (pod.istioInitContainers && pod.istioInitContainers.some(cont => cont.name === 'istio-proxy')) {
           hasIstioSidecars = true;
         } else {
+          // Ztunnel doesn't have Envoy
           hasIstioSidecars =
-            hasIstioSidecars || (!!pod.containers && pod.containers.some(cont => cont.name === 'istio-proxy'));
+            hasIstioSidecars ||
+            (!!pod.containers &&
+              pod.containers.some(cont => cont.name === 'istio-proxy' && workload.ambient !== 'ztunnel'));
         }
       });
     }
diff --git a/frontend/src/pages/WorkloadDetails/WorkloadPods.tsx b/frontend/src/pages/WorkloadDetails/WorkloadPods.tsx
index be844c70e..2892a6333 100644
--- a/frontend/src/pages/WorkloadDetails/WorkloadPods.tsx
+++ b/frontend/src/pages/WorkloadDetails/WorkloadPods.tsx
@@ -105,6 +105,11 @@ export const WorkloadPods: React.FC<WorkloadPodsProps> = (props: WorkloadPodsPro
               </div>
             </li>
 
+            <li>
+              <span>Protocol</span>
+              <div style={{ display: 'inline-block' }}>{pod.protocol ? pod.protocol : 'TCP'}</div>
+            </li>
+
             <li>
               <span>Labels</span>
               <div style={{ display: 'inline-block' }}>
diff --git a/frontend/src/pages/WorkloadList/WorkloadListPage.tsx b/frontend/src/pages/WorkloadList/WorkloadListPage.tsx
index 5f37dc83d..89c14a246 100644
--- a/frontend/src/pages/WorkloadList/WorkloadListPage.tsx
+++ b/frontend/src/pages/WorkloadList/WorkloadListPage.tsx
@@ -116,6 +116,7 @@ class WorkloadListPageComponent extends FilterComponent.Component<
   getDeploymentItems(data: ClusterWorkloadsResponse): WorkloadListItem[] {
     if (data.workloads) {
       return data.workloads.map(deployment => ({
+        ambient: deployment.ambient,
         cluster: deployment.cluster,
         namespace: deployment.namespace,
         name: deployment.name,
diff --git a/frontend/src/services/Api.ts b/frontend/src/services/Api.ts
index 5e733a607..ffe2dca44 100644
--- a/frontend/src/services/Api.ts
+++ b/frontend/src/services/Api.ts
@@ -43,7 +43,8 @@ import {
   PodLogsQuery,
   LogLevelQuery,
   LogType,
-  GroupVersionKind
+  GroupVersionKind,
+  ZtunnelConfigDump
 } from '../types/IstioObjects';
 import { ComponentStatus, IstiodResourceThresholds } from '../types/IstioStatus';
 import { TracingInfo, TracingResponse, TracingSingleResponse } from '../types/TracingInfo';
@@ -1132,6 +1133,20 @@ export const getPodEnvoyProxy = (
   return newRequest<EnvoyProxyDump>(HTTP_VERBS.GET, urls.podEnvoyProxy(namespace, pod), params, {});
 };
 
+export const getPodZtunnelConfig = (
+  namespace: string,
+  pod: string,
+  cluster?: string
+): Promise<ApiResponse<ZtunnelConfigDump>> => {
+  const params: ClusterParam = {};
+
+  if (cluster) {
+    params.clusterName = cluster;
+  }
+
+  return newRequest<ZtunnelConfigDump>(HTTP_VERBS.GET, urls.podZtunnelConfig(namespace, pod), params, {});
+};
+
 export const getPodEnvoyProxyResourceEntries = (
   namespace: string,
   pod: string,
diff --git a/frontend/src/types/App.ts b/frontend/src/types/App.ts
index 7339b2f3a..cc57af922 100644
--- a/frontend/src/types/App.ts
+++ b/frontend/src/types/App.ts
@@ -10,6 +10,7 @@ export type AppId = {
 };
 
 export interface AppWorkload {
+  ambient?: string;
   isAmbient: boolean;
   isGateway: boolean;
   istioSidecar: boolean;
diff --git a/frontend/src/types/AppList.ts b/frontend/src/types/AppList.ts
index f3db2b257..ebf3f3d79 100644
--- a/frontend/src/types/AppList.ts
+++ b/frontend/src/types/AppList.ts
@@ -8,6 +8,7 @@ export interface AppList {
 }
 
 export interface AppListItem {
+  ambient?: string;
   cluster?: string;
   health: AppHealth;
   instanceType: InstanceType.App;
diff --git a/frontend/src/types/Health.ts b/frontend/src/types/Health.ts
index dde57389d..8f88f76b4 100644
--- a/frontend/src/types/Health.ts
+++ b/frontend/src/types/Health.ts
@@ -2,6 +2,7 @@ import {
   CheckCircleIcon,
   ExclamationCircleIcon,
   ExclamationTriangleIcon,
+  InfoCircleIcon,
   MinusCircleIcon,
   UnknownIcon
 } from '@patternfly/react-icons';
@@ -115,6 +116,14 @@ export const DEGRADED: Status = {
   priority: 3
 };
 
+export const INFO: Status = {
+  className: 'icon-info',
+  color: PFColors.Info,
+  icon: InfoCircleIcon,
+  name: t('Info'),
+  priority: 2
+};
+
 export const NOT_READY: Status = {
   className: 'icon-idle',
   color: PFColors.Info,
@@ -225,6 +234,16 @@ export const proxyStatusMessage = (syncedProxies: number, desiredReplicas: numbe
   return msg;
 };
 
+export const hasProxyStatusInfoSeverity = (status: ProxyStatus): boolean => {
+  return (
+    isProxyStatusComponentSyncedOrIgnored(status.CDS) &&
+    isProxyStatusComponentSyncedOrIgnored(status.EDS) &&
+    isProxyStatusComponentSyncedOrIgnored(status.LDS) &&
+    isProxyStatusComponentSyncedOrIgnored(status.RDS) &&
+    !isProxyStatusSynced(status)
+  );
+};
+
 export const isProxyStatusSynced = (status: ProxyStatus): boolean => {
   return (
     isProxyStatusComponentSynced(status.CDS) &&
@@ -235,7 +254,11 @@ export const isProxyStatusSynced = (status: ProxyStatus): boolean => {
 };
 
 export const isProxyStatusComponentSynced = (componentStatus: string): boolean => {
-  return componentStatus === 'Synced';
+  return componentStatus.toLowerCase() === 'synced';
+};
+
+export const isProxyStatusComponentSyncedOrIgnored = (componentStatus: string): boolean => {
+  return componentStatus.toLowerCase() === 'synced' || componentStatus.toLowerCase() === 'ignored';
 };
 
 export const mergeStatus = (s1: Status, s2: Status): Status => {
diff --git a/frontend/src/types/IstioObjects.ts b/frontend/src/types/IstioObjects.ts
index 22007064d..1fe871505 100644
--- a/frontend/src/types/IstioObjects.ts
+++ b/frontend/src/types/IstioObjects.ts
@@ -177,6 +177,7 @@ export interface Pod {
   istioInitContainers?: ContainerInfo[];
   labels?: { [key: string]: string };
   name: string;
+  protocol?: string;
   proxyStatus?: ProxyStatus;
   serviceAccountName: string;
   status: string;
@@ -1433,3 +1434,167 @@ export interface APIKey {
 
 export const MAX_PORT = 65535;
 export const MIN_PORT = 0;
+
+export type ZtunnelConfigDump = {
+  certificates?: Certificate[];
+  config?: ZtunnelConfig;
+  policies?: any[];
+  services?: ZtunnelService[];
+  workloads?: ZtunnelWorkload[];
+};
+
+export type Certificate = {
+  certChain: CertChain[];
+  identity: string;
+  state: string;
+};
+
+export type CertChain = {
+  expirationTime: string;
+  pem: string;
+  serialNumber?: string;
+  validFrom: string;
+};
+
+export type ZtunnelConfig = {
+  adminAddr: ZtunnelAddress;
+  altCaHostname?: string;
+  altXdsHostname?: string;
+  caAddress: string;
+  caRootCert: FilePath;
+  clusterDomain: string;
+  clusterId: string;
+  connectionWindowSize: number;
+  dnsProxy: boolean;
+  dnsProxyAddr: ZtunnelAddress;
+  dnsResolverCfg: DNSResolverConfig;
+  dnsResolverOpts: DNSResolverOptions;
+  fakeCa: boolean;
+  fakeSelfInbound: boolean;
+  frameSize: number;
+  illegalPorts: number[];
+  inboundAddr: string;
+  inboundPlaintextAddr: string;
+  inpodPortReuse: boolean;
+  inpodUds: string;
+  localNode: string;
+  network: string;
+  numWorkerThreads: number;
+  outboundAddr: string;
+  packetMark: number;
+  poolMaxStreamsPerConn: number;
+  poolUnusedReleaseTimeout: TimeDuration;
+  proxy: boolean;
+  proxyArgs: string;
+  proxyMetadata: Record<string, string>;
+  proxyMode: string;
+  proxyWorkloadInformation?: string;
+  readinessAddr: SocketAddress;
+  requireOriginalSource?: string;
+  secretTtl: TimeDuration;
+  selfTerminationDeadline: TimeDuration;
+  socks5Addr?: string;
+  statsAddr: SocketAddress;
+  windowSize: number;
+  xdsAddress: string;
+  xdsOnDemand: boolean;
+  xdsRootCert: FilePath;
+};
+
+export type ZtunnelAddress = {
+  Localhost: any[];
+};
+
+export type FilePath = {
+  File: string;
+};
+
+export type DNSResolverConfig = {
+  domain?: string;
+  name_servers: NameServer[];
+  search: string[];
+};
+
+export type NameServer = {
+  bind_addr?: string;
+  protocol: string;
+  socket_addr: string;
+  tls_dns_name?: string;
+  trust_negative_responses: boolean;
+};
+
+export type DNSResolverOptions = {
+  attempts: number;
+  authentic_data: boolean;
+  cache_size: number;
+  check_names: boolean;
+  edns0: boolean;
+  ip_strategy: string;
+  ndots: number;
+  negative_max_ttl?: string;
+  negative_min_ttl?: string;
+  num_concurrent_reqs: number;
+  positive_max_ttl?: string;
+  positive_min_ttl?: string;
+  preserve_intermediates: boolean;
+  recursion_desired: boolean;
+  rotate: boolean;
+  server_ordering_strategy: string;
+  shuffle_dns_servers: boolean;
+  timeout: TimeDuration;
+  try_tcp_on_error: boolean;
+  use_hosts_file: boolean;
+  validate: boolean;
+};
+
+export type TimeDuration = {
+  nanos: number;
+  secs: number;
+};
+
+export type SocketAddress = {
+  SocketAddr: string;
+};
+
+export type ZtunnelService = {
+  endpoints: Record<string, ZtunnelEndpoint>;
+  hostname: string;
+  ipFamilies: string;
+  name: string;
+  namespace: string;
+  ports: Record<string, number>;
+  subjectAltNames: string[];
+  vips: string[];
+  waypoint: ZtunnelWaypoint;
+};
+
+export type ZtunnelEndpoint = {
+  port: Record<string, number>;
+  status: string;
+  workloadUid: string;
+};
+
+export type ZtunnelWaypoint = {
+  destination: string;
+  hboneMtlsPort: number;
+};
+
+export type ZtunnelWorkload = {
+  canonicalName: string;
+  canonicalRevision: string;
+  clusterId: string;
+  name: string;
+  namespace: string;
+  networkMode: string;
+  node: string;
+  protocol: string;
+  serviceAccount: string;
+  services: string[];
+  status: string;
+  trustDomain: string;
+  uid: string;
+  waypoint?: ZtunnelWaypoint;
+  workloadIps: string[];
+  workloadName: string;
+  workloadType: string;
+};
diff --git a/frontend/src/types/ServiceInfo.ts b/frontend/src/types/ServiceInfo.ts
index b56415c81..394a4e325 100644
--- a/frontend/src/types/ServiceInfo.ts
+++ b/frontend/src/types/ServiceInfo.ts
@@ -1,4 +1,4 @@
-import { DEGRADED, FAILURE, HEALTHY, NA, ServiceHealth, Status } from './Health';
+import { DEGRADED, FAILURE, HEALTHY, INFO, NA, ServiceHealth, Status } from './Health';
 import {
   DestinationRule,
   getWizardUpdateLabel,
@@ -45,6 +45,7 @@ interface EndpointAddress {
 }
 
 export interface WorkloadOverview {
+  ambient?: string;
   createdAt: string;
   isAmbient: boolean;
   isGateway: boolean;
@@ -57,6 +58,7 @@ export interface WorkloadOverview {
   type: string;
 }
 
+export type IPFamily = 'IPv4' | 'IPv6';
 export interface Service {
   additionalDetails: AdditionalItem[];
   annotations: { [key: string]: string };
@@ -64,6 +66,8 @@ export interface Service {
   createdAt: string;
   externalName: string;
   ip: string;
+  ips?: string[]; // present in dual stack. ip === ips[0]
+  ipFamilies?: IPFamily[]; // ipFamilies[0] represents ip, ipFamilies[i] represents ips[i]
   labels?: { [key: string]: string };
   name: string;
   namespace: string;
@@ -188,6 +192,8 @@ export const validationToHealth = (severity: ValidationTypes): Status => {
 
   if (severity === ValidationTypes.Correct) {
     status = HEALTHY;
+  } else if (severity === ValidationTypes.Info) {
+    status = INFO;
   } else if (severity === ValidationTypes.Warning) {
     status = DEGRADED;
   } else if (severity === ValidationTypes.Error) {
diff --git a/frontend/src/types/ServiceList.ts b/frontend/src/types/ServiceList.ts
index 726bec30c..f1a33b4b7 100644
--- a/frontend/src/types/ServiceList.ts
+++ b/frontend/src/types/ServiceList.ts
@@ -11,6 +11,7 @@ export interface ServiceList {
 
 export interface ServiceOverview {
   additionalDetailSample?: AdditionalItem;
+  ambient?: string;
   cluster?: string;
   health: ServiceHealth;
   isAmbient: boolean;
diff --git a/frontend/src/types/Workload.ts b/frontend/src/types/Workload.ts
index 31d563138..f56974c0a 100644
--- a/frontend/src/types/Workload.ts
+++ b/frontend/src/types/Workload.ts
@@ -9,6 +9,7 @@ export type WorkloadId = {
 
 export interface Workload {
   additionalDetails: AdditionalItem[];
+  ambient?: string;
   annotations: { [key: string]: string };
   appLabel: boolean;
   availableReplicas: Number;
@@ -59,6 +60,7 @@ export const emptyWorkload: Workload = {
 
 export interface WorkloadListItem {
   additionalDetailSample?: AdditionalItem;
+  ambient?: string;
   appLabel: boolean;
   cluster?: string;
   gvk: GroupVersionKind;
diff --git a/frontend/src/utils/IstioConfigUtils.ts b/frontend/src/utils/IstioConfigUtils.ts
index 21cdf75e6..fbc55bb95 100644
--- a/frontend/src/utils/IstioConfigUtils.ts
+++ b/frontend/src/utils/IstioConfigUtils.ts
@@ -48,7 +48,7 @@ export const isK8sGatewayHostValid = (k8sGatewayHost: string): boolean => {
     return false;
   }
 
-  // K8s gateway host must be fqdn but not ip address
+  // K8s gateway host must be fqdn but not ip address (ipv4 or ipv6)
   if (k8sGatewayHost.split('.').length < 2 || k8sGatewayHost.search(ipRegexp) === 0) {
     return false;
   }
diff --git a/go.mod b/go.mod
index 597da0035..48c62a882 100644
--- a/go.mod
+++ b/go.mod
@@ -28,9 +28,8 @@ require (
 	go.opentelemetry.io/otel/trace v1.28.0
 	go.uber.org/automaxprocs v1.5.3
 	golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f
-	golang.org/x/net v0.28.0
 	golang.org/x/oauth2 v0.21.0
-	golang.org/x/sync v0.8.0
+	golang.org/x/sync v0.10.0
 	google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117
 	google.golang.org/grpc v1.66.2
 	google.golang.org/protobuf v1.34.2
@@ -89,10 +88,11 @@ require (
 	go.opentelemetry.io/proto/otlp v1.3.1 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	go.uber.org/zap v1.26.0 // indirect
-	golang.org/x/crypto v0.26.0 // indirect
-	golang.org/x/sys v0.24.0 // indirect
-	golang.org/x/term v0.23.0 // indirect
-	golang.org/x/text v0.17.0 // indirect
+	golang.org/x/crypto v0.31.0 // indirect
+	golang.org/x/net v0.33.0 // indirect
+	golang.org/x/sys v0.28.0 // indirect
+	golang.org/x/term v0.27.0 // indirect
+	golang.org/x/text v0.21.0 // indirect
 	golang.org/x/time v0.5.0 // indirect
 	golang.org/x/tools v0.24.0 // indirect
 	gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
diff --git a/go.sum b/go.sum
index 8ebc200cb..a1ba3743f 100644
--- a/go.sum
+++ b/go.sum
@@ -172,8 +172,8 @@ go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
-golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
+golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
+golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
 golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY=
 golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -182,26 +182,26 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
-golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
+golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
+golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
 golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
 golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
-golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
+golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
-golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
-golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
+golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
+golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
+golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
-golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
+golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
+golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
 golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
 golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
diff --git a/graph/options.go b/graph/options.go
index 99271c6c4..85237c68b 100644
--- a/graph/options.go
+++ b/graph/options.go
@@ -63,7 +63,7 @@ type NodeOptions struct {
 	AggregateValue string
 	App            string
 	Cluster        string
-	Namespace      string
+	Namespace      NamespaceInfo
 	Service        string
 	Version        string
 	Workload       string
@@ -106,6 +106,7 @@ func GetClusterSensitiveKey(cluster, name string) ClusterSensitiveKey {
 type AccessibleNamespace struct {
 	Cluster           string
 	CreationTimestamp time.Time
+	IsAmbient         bool
 	Name              string
 }
 
@@ -271,20 +272,23 @@ func NewOptions(r *net_http.Request, namespacesService *business.NamespaceServic
 	for _, namespaceName := range strings.Split(namespaces, ",") {
 		namespaceName = strings.TrimSpace(namespaceName)
 		var earliestCreationTimestamp *time.Time
+		var isAmbient bool
 		for _, an := range accessibleNamespaces {
 			if namespaceName == an.Name {
 				if nil == earliestCreationTimestamp || earliestCreationTimestamp.After(an.CreationTimestamp) {
 					earliestCreationTimestamp = &an.CreationTimestamp
 				}
+				isAmbient = an.IsAmbient
 			}
 		}
 		if nil == earliestCreationTimestamp {
 			Forbidden(fmt.Sprintf("Requested namespace [%s] is not accessible.", namespaceName))
 		} else {
 			namespaceMap[namespaceName] = NamespaceInfo{
-				Name:     namespaceName,
-				Duration: getSafeNamespaceDuration(namespaceName, *earliestCreationTimestamp, time.Duration(duration), queryTime),
-				IsIstio:  config.IsIstioNamespace(namespaceName),
+				Name:      namespaceName,
+				Duration:  getSafeNamespaceDuration(namespaceName, *earliestCreationTimestamp, time.Duration(duration), queryTime),
+				IsAmbient: isAmbient,
+				IsIstio:   config.IsIstioNamespace(namespaceName),
 			}
 		}
 	}
@@ -391,7 +395,7 @@ func NewOptions(r *net_http.Request, namespacesService *business.NamespaceServic
 				AggregateValue: aggregateValue,
 				App:            app,
 				Cluster:        cluster,
-				Namespace:      namespace,
+				Namespace:      namespaceMap[namespace],
 				Service:        service,
 				Version:        version,
 				Workload:       workload,
@@ -427,6 +431,7 @@ func getAccessibleNamespaces(ctx context.Context, namespacesService *business.Na
 		accessibleNamespaces[GetClusterSensitiveKey(namespace.Cluster, namespace.Name)] = &AccessibleNamespace{
 			Cluster:           namespace.Cluster,
 			CreationTimestamp: namespace.CreationTimestamp,
+			IsAmbient:         namespace.IsAmbient,
 			Name:              namespace.Name,
 		}
 	}
diff --git a/graph/telemetry/istio/appender/aggregate_node.go b/graph/telemetry/istio/appender/aggregate_node.go
index cc6161e3f..fecbf7ee7 100644
--- a/graph/telemetry/istio/appender/aggregate_node.go
+++ b/graph/telemetry/istio/appender/aggregate_node.go
@@ -18,7 +18,7 @@ const (
 
 // AggregateNodeAppender is responsible for injecting aggregate nodes into the graph to gain
 // visibility into traffic aggregations for a user-specfied metric attribute.
-// Note: Aggregate Nodes are supported only on Requests traffic (not TCP or gRPC-message traffic)
+// Note: Aggregate Nodes are supported only on Request traffic (not TCP or gRPC-message traffic)
 type AggregateNodeAppender struct {
 	Aggregate          string
 	AggregateValue     string
@@ -79,6 +79,10 @@ func (a AggregateNodeAppender) appendGraph(trafficMap graph.TrafficMap, namespac
 	//   note2: for now we will filter out aggregates with no traffic on the assumption that users probably don't want to
 	//      see them and it will just increase the graph density.  To change that behavior remove the "> 0" conditions.
 	// 1) query for requests originating from a workload outside the namespace.
+	//
+	// TODO: This *may* require an additional query to pick up incoming gateway traffic (source reported) for ambient namespaces (no dest
+	// proxy reporting) but because it's unclear whether this is a used feature, or whether we really need to handle that use case, I'm
+	// deferring. If necessary, see the incoming traffic handling in buildNamespacesTrafficMap.
 	groupBy := fmt.Sprintf("source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags,%s", a.Aggregate)
 	httpQuery := fmt.Sprintf(`sum(rate(%s{%s,source_workload_namespace!="%s",destination_service_namespace="%v",%s!="unknown"}[%vs])) by (%s) > 0`,
 		"istio_requests_total",
diff --git a/graph/telemetry/istio/appender/response_time.go b/graph/telemetry/istio/appender/response_time.go
index 2b48c9f68..464c02a0a 100644
--- a/graph/telemetry/istio/appender/response_time.go
+++ b/graph/telemetry/istio/appender/response_time.go
@@ -62,10 +62,11 @@ func (a ResponseTimeAppender) AppendGraph(trafficMap graph.TrafficMap, globalInf
 		graph.CheckError(err)
 	}
 
-	a.appendGraph(trafficMap, namespaceInfo.Namespace, globalInfo.PromClient)
+	a.appendGraph(trafficMap, a.Namespaces[namespaceInfo.Namespace], globalInfo.PromClient)
 }
 
-func (a ResponseTimeAppender) appendGraph(trafficMap graph.TrafficMap, namespace string, client *prometheus.Client) {
+func (a ResponseTimeAppender) appendGraph(trafficMap graph.TrafficMap, namespaceInfo graph.NamespaceInfo, client *prometheus.Client) {
+	namespace := namespaceInfo.Name
 	// create map to quickly look up responseTime
 	responseTimeMap := make(map[string]float64)
 	duration := a.Namespaces[namespace].Duration
@@ -77,8 +78,25 @@ func (a ResponseTimeAppender) appendGraph(trafficMap graph.TrafficMap, namespace
 		// query prometheus for the responseTime info in two queries:
 		groupBy := "source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol"
 
+		// 0) Incoming: Ambient only: query source telemetry, typically from a non-waypoint ingress gateway, that will likely not have overlapping dest or waypoint telem for the traffic (that traffic will be picked up in query #2)
+		if namespaceInfo.IsAmbient {
+			query := fmt.Sprintf(`sum(rate(%s{reporter="source",source_workload_namespace!="%s",destination_service_namespace="%s"}[%vs])) by (%s) / sum(rate(%s{reporter="source",source_workload_namespace!="%s",destination_service_namespace="%s"}[%vs])) by (%s) > 0`,
+				"istio_request_duration_milliseconds_sum",
+				namespace,
+				namespace,
+				int(duration.Seconds()), // range duration for the query
+				groupBy,
+				"istio_request_duration_milliseconds_count",
+				namespace,
+				namespace,
+				int(duration.Seconds()), // range duration for the query
+				groupBy)
+			incomingVector := promQuery(query, time.Unix(a.QueryTime, 0), client.GetContext(), client.API(), a)
+			a.populateResponseTimeMap(responseTimeMap, &incomingVector)
+		}
+
 		// 1) Incoming: query destination telemetry to capture namespace services' incoming traffic
-		// note - the query order is important as both queries may have overlapping results for edges within
+		// note - the order of the next two queries is important as both queries may have overlapping results for edges within
 		//        the namespace.  This query uses destination proxy and so must come first.
 		query := fmt.Sprintf(`sum(rate(%s{%s,destination_service_namespace="%s"}[%vs])) by (%s) / sum(rate(%s{%s,destination_service_namespace="%s"}[%vs])) by (%s) > 0`,
 			"istio_request_duration_milliseconds_sum",
@@ -115,6 +133,19 @@ func (a ResponseTimeAppender) appendGraph(trafficMap graph.TrafficMap, namespace
 		// query prometheus for the responseTime info in two queries:
 		groupBy := "le,source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol"
 
+		// 0) Incoming: Ambient only: query source telemetry, typically from a non-waypoint ingress gateway, that will likely not have overlapping dest or waypoint telem for the traffic (that traffic will be picked up in query #2)
+		if namespaceInfo.IsAmbient {
+			query := fmt.Sprintf(`histogram_quantile(%.2f, sum(rate(%s{reporter="source",source_workload_namespace!="%s",destination_service_namespace="%s"}[%vs])) by (%s)) > 0`,
+				quantile,
+				"istio_request_duration_milliseconds_bucket",
+				namespace,
+				namespace,
+				int(duration.Seconds()), // range duration for the query
+				groupBy)
+			incomingVector := promQuery(query, time.Unix(a.QueryTime, 0), client.GetContext(), client.API(), a)
+			a.populateResponseTimeMap(responseTimeMap, &incomingVector)
+		}
+
 		// 1) Incoming: query destination telemetry to capture namespace services' incoming traffic
 		// note - the query order is important as both queries may have overlapping results for edges within
 		//        the namespace.  This query uses destination proxy and so must come first.
diff --git a/graph/telemetry/istio/istio.go b/graph/telemetry/istio/istio.go
index f2c4a65ff..c8bdea40a 100644
--- a/graph/telemetry/istio/istio.go
+++ b/graph/telemetry/istio/istio.go
@@ -76,26 +76,26 @@ func BuildNamespacesTrafficMap(ctx context.Context, o graph.TelemetryOptions, gl
 		globalInfo.Vendor[appender.AmbientWaypoints] = waypoints
 	}
 
-	for _, namespace := range o.Namespaces {
-		log.Tracef("Build traffic map for namespace [%v]", namespace)
-		namespaceTrafficMap := buildNamespaceTrafficMap(ctx, namespace.Name, o, globalInfo)
+	for _, namespaceInfo := range o.Namespaces {
+		log.Tracef("Build traffic map for namespace [%v]", namespaceInfo)
+		namespaceTrafficMap := buildNamespaceTrafficMap(ctx, namespaceInfo, o, globalInfo)
 
 		// The appenders can add/remove/alter nodes for the namespace
-		namespaceInfo := graph.NewAppenderNamespaceInfo(namespace.Name)
+		appenderNamespaceInfo := graph.NewAppenderNamespaceInfo(namespaceInfo.Name)
 		for _, a := range appenders {
 			var appenderEnd observability.EndFunc
 			_, appenderEnd = observability.StartSpan(ctx, "Appender "+a.Name(),
 				observability.Attribute("package", "istio"),
-				observability.Attribute("namespace", namespace.Name),
+				observability.Attribute("namespace", namespaceInfo.Name),
 			)
 			appenderTimer := internalmetrics.GetGraphAppenderTimePrometheusTimer(a.Name())
-			a.AppendGraph(namespaceTrafficMap, globalInfo, namespaceInfo)
+			a.AppendGraph(namespaceTrafficMap, globalInfo, appenderNamespaceInfo)
 			appenderTimer.ObserveDuration()
 			appenderEnd()
 		}
 
 		// Merge this namespace into the final TrafficMap
-		telemetry.MergeTrafficMaps(trafficMap, namespace.Name, namespaceTrafficMap)
+		telemetry.MergeTrafficMaps(trafficMap, namespaceInfo.Name, namespaceTrafficMap)
 	}
 
 	// The finalizers can perform final manipulations on the complete graph
@@ -112,7 +112,8 @@ func BuildNamespacesTrafficMap(ctx context.Context, o graph.TelemetryOptions, gl
 
 // buildNamespaceTrafficMap returns a map of all namespace nodes (key=id).  All
 // nodes either directly send and/or receive requests from a node in the namespace.
-func buildNamespaceTrafficMap(ctx context.Context, namespace string, o graph.TelemetryOptions, globalInfo *graph.GlobalInfo) graph.TrafficMap {
+func buildNamespaceTrafficMap(ctx context.Context, namespaceInfo graph.NamespaceInfo, o graph.TelemetryOptions, globalInfo *graph.GlobalInfo) graph.TrafficMap {
+	namespace := namespaceInfo.Name
 	var end observability.EndFunc
 	_, end = observability.StartSpan(ctx, "buildNamespaceTrafficMap",
 		observability.Attribute("package", "istio"),
@@ -128,6 +129,8 @@ func buildNamespaceTrafficMap(ctx context.Context, namespace string, o graph.Tel
 		idleCondition = ""
 	}
 	promApi := globalInfo.PromClient.API()
+	var query string
+	var trafficVector model.Vector
 
 	// HTTP/GRPC request traffic
 	if o.Rates.Http == graph.RateRequests || o.Rates.Grpc == graph.RateRequests {
@@ -135,7 +138,7 @@ func buildNamespaceTrafficMap(ctx context.Context, namespace string, o graph.Tel
 		groupBy := "source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags"
 
 		// 0) Incoming: query source telemetry to capture unserviced namespace services' incoming traffic (failed requests that never reach a dest)
-		query := fmt.Sprintf(`sum(rate(%s{%s,source_workload_namespace!="%s",destination_workload_namespace="unknown",destination_workload="unknown",destination_service=~"^.+\\.%s\\..+$"} [%vs])) by (%s) %s`,
+		query = fmt.Sprintf(`sum(rate(%s{%s,source_workload_namespace!="%s",destination_workload_namespace="unknown",destination_workload="unknown",destination_service=~"^.+\\.%s\\..+$"} [%vs])) by (%s) %s`,
 			metric,
 			util.GetReporter("source", o.Rates),
 			namespace,
@@ -143,10 +146,23 @@ func buildNamespaceTrafficMap(ctx context.Context, namespace string, o graph.Tel
 			int(duration.Seconds()), // range duration for the query
 			groupBy,
 			idleCondition)
-		incomingVector := promQuery(query, time.Unix(o.QueryTime, 0), promApi)
-		populateTrafficMap(trafficMap, &incomingVector, metric, o, globalInfo)
+		trafficVector = promQuery(query, time.Unix(o.QueryTime, 0), promApi)
+		populateTrafficMap(trafficMap, &trafficVector, metric, o, globalInfo)
 
-		// 1) Incoming: query destination telemetry to capture namespace services' incoming traffic
+		// 1) Incoming: Ambient only: query source telemetry, typically from a non-waypoint ingress gateway, that will likely not have overlapping dest or waypoint telem for the traffic (that traffic will be picked up in query #2)
+		if namespaceInfo.IsAmbient {
+			query = fmt.Sprintf(`sum(rate(%s{reporter="source",source_workload_namespace!="%s",destination_workload_namespace="%s"} [%vs])) by (%s) %s`,
+				metric,
+				namespace,
+				namespace,
+				int(duration.Seconds()), // range duration for the query
+				groupBy,
+				idleCondition)
+			trafficVector = promQuery(query, time.Unix(o.QueryTime, 0), promApi)
+			populateTrafficMap(trafficMap, &trafficVector, metric, o, globalInfo)
+		}
+
+		// 2) Incoming: query destination telemetry to capture namespace services' incoming traffic
 		query = fmt.Sprintf(`sum(rate(%s{%s,destination_workload_namespace="%s"} [%vs])) by (%s) %s`,
 			metric,
 			util.GetReporter("destination", o.Rates),
@@ -154,10 +170,10 @@ func buildNamespaceTrafficMap(ctx context.Context, namespace string, o graph.Tel
 			int(duration.Seconds()), // range duration for the query
 			groupBy,
 			idleCondition)
-		incomingVector = promQuery(query, time.Unix(o.QueryTime, 0), promApi)
-		populateTrafficMap(trafficMap, &incomingVector, metric, o, globalInfo)
+		trafficVector = promQuery(query, time.Unix(o.QueryTime, 0), promApi)
+		populateTrafficMap(trafficMap, &trafficVector, metric, o, globalInfo)
 
-		// 2) Outgoing: query source telemetry to capture namespace workloads' outgoing traffic
+		// 3) Outgoing: query source telemetry to capture namespace workloads' outgoing traffic
 		query = fmt.Sprintf(`sum(rate(%s{%s,source_workload_namespace="%s"} [%vs])) by (%s) %s`,
 			metric,
 			util.GetReporter("source", o.Rates),
@@ -165,8 +181,8 @@ func buildNamespaceTrafficMap(ctx context.Context, namespace string, o graph.Tel
 			int(duration.Seconds()), // range duration for the query
 			groupBy,
 			idleCondition)
-		outgoingVector := promQuery(query, time.Unix(o.QueryTime, 0), promApi)
-		populateTrafficMap(trafficMap, &outgoingVector, metric, o, globalInfo)
+		trafficVector = promQuery(query, time.Unix(o.QueryTime, 0), promApi)
+		populateTrafficMap(trafficMap, &trafficVector, metric, o, globalInfo)
 	}
 
 	// GRPC Message traffic
@@ -187,7 +203,7 @@ func buildNamespaceTrafficMap(ctx context.Context, namespace string, o graph.Tel
 
 		for _, metric := range metrics {
 			// 0) Incoming: query source telemetry to capture unserviced namespace services' incoming traffic
-			query := fmt.Sprintf(`sum(rate(%s{%s,source_workload_namespace!="%s",destination_workload_namespace="unknown",destination_workload="unknown",destination_service=~"^.+\\.%s\\..+$"} [%vs])) by (%s) %s`,
+			query = fmt.Sprintf(`sum(rate(%s{%s,source_workload_namespace!="%s",destination_workload_namespace="unknown",destination_workload="unknown",destination_service=~"^.+\\.%s\\..+$"} [%vs])) by (%s) %s`,
 				metric,
 				util.GetReporter("source", o.Rates),
 				namespace,
@@ -195,10 +211,23 @@ func buildNamespaceTrafficMap(ctx context.Context, namespace string, o graph.Tel
 				int(duration.Seconds()), // range duration for the query
 				groupBy,
 				idleCondition)
-			incomingVector := promQuery(query, time.Unix(o.QueryTime, 0), promApi)
-			populateTrafficMap(trafficMap, &incomingVector, metric, o, globalInfo)
+			trafficVector = promQuery(query, time.Unix(o.QueryTime, 0), promApi)
+			populateTrafficMap(trafficMap, &trafficVector, metric, o, globalInfo)
 
-			// 1) Incoming: query destination telemetry to capture namespace services' incoming traffic	query = fmt.Sprintf(`sum(rate(%s{reporter="destination",destination_service_namespace="%s"} [%vs])) by (%s) %s`,
+			// 1) Incoming: Ambient only: query source telemetry, typically from a non-waypoint ingress gateway, that will likely not have overlapping dest or waypoint telem for the traffic (that traffic will be picked up in query #2)
+			if namespaceInfo.IsAmbient {
+				query = fmt.Sprintf(`sum(rate(%s{reporter="source",source_workload_namespace!="%s",destination_workload_namespace="%s"} [%vs])) by (%s) %s`,
+					metric,
+					namespace,
+					namespace,
+					int(duration.Seconds()), // range duration for the query
+					groupBy,
+					idleCondition)
+				trafficVector = promQuery(query, time.Unix(o.QueryTime, 0), promApi)
+				populateTrafficMap(trafficMap, &trafficVector, metric, o, globalInfo)
+			}
+
+			// 2) Incoming: query destination telemetry to capture namespace services' incoming traffic	query = fmt.Sprintf(`sum(rate(%s{reporter="destination",destination_service_namespace="%s"} [%vs])) by (%s) %s`,
 			query = fmt.Sprintf(`sum(rate(%s{%s,destination_workload_namespace="%s"} [%vs])) by (%s) %s`,
 				metric,
 				util.GetReporter("destination", o.Rates),
@@ -206,10 +235,10 @@ func buildNamespaceTrafficMap(ctx context.Context, namespace string, o graph.Tel
 				int(duration.Seconds()), // range duration for the query
 				groupBy,
 				idleCondition)
-			incomingVector = promQuery(query, time.Unix(o.QueryTime, 0), promApi)
-			populateTrafficMap(trafficMap, &incomingVector, metric, o, globalInfo)
+			trafficVector = promQuery(query, time.Unix(o.QueryTime, 0), promApi)
+			populateTrafficMap(trafficMap, &trafficVector, metric, o, globalInfo)
 
-			// 2) Outgoing: query source telemetry to capture namespace workloads' outgoing traffic
+			// 3) Outgoing: query source telemetry to capture namespace workloads' outgoing traffic
 			query = fmt.Sprintf(`sum(rate(%s{%s,source_workload_namespace="%s"} [%vs])) by (%s) %s`,
 				metric,
 				util.GetReporter("source", o.Rates),
@@ -217,8 +246,8 @@ func buildNamespaceTrafficMap(ctx context.Context, namespace string, o graph.Tel
 				int(duration.Seconds()), // range duration for the query
 				groupBy,
 				idleCondition)
-			outgoingVector := promQuery(query, time.Unix(o.QueryTime, 0), promApi)
-			populateTrafficMap(trafficMap, &outgoingVector, metric, o, globalInfo)
+			trafficVector = promQuery(query, time.Unix(o.QueryTime, 0), promApi)
+			populateTrafficMap(trafficMap, &trafficVector, metric, o, globalInfo)
 		}
 	}
 
@@ -241,7 +270,7 @@ func buildNamespaceTrafficMap(ctx context.Context, namespace string, o graph.Tel
 
 		for _, metric := range metrics {
 			// 0) Incoming: query source telemetry to capture unserviced namespace services' incoming traffic
-			query := fmt.Sprintf(`sum(rate(%s{%s%s,source_workload_namespace!="%s",destination_workload_namespace="unknown",destination_workload="unknown",destination_service=~"^.+\\.%s\\..+$"} [%vs])) by (%s) %s`,
+			query = fmt.Sprintf(`sum(rate(%s{%s%s,source_workload_namespace!="%s",destination_workload_namespace="unknown",destination_workload="unknown",destination_service=~"^.+\\.%s\\..+$"} [%vs])) by (%s) %s`,
 				metric,
 				util.GetApp(o.Rates),
 				util.GetReporter("source", o.Rates),
@@ -250,8 +279,8 @@ func buildNamespaceTrafficMap(ctx context.Context, namespace string, o graph.Tel
 				int(duration.Seconds()), // range duration for the query
 				groupBy,
 				idleCondition)
-			incomingVector := promQuery(query, time.Unix(o.QueryTime, 0), promApi)
-			populateTrafficMap(trafficMap, &incomingVector, metric, o, globalInfo)
+			trafficVector = promQuery(query, time.Unix(o.QueryTime, 0), promApi)
+			populateTrafficMap(trafficMap, &trafficVector, metric, o, globalInfo)
 
 			// 1) Incoming: query destination telemetry to capture namespace services' incoming traffic	query = fmt.Sprintf(`sum(rate(%s{reporter="destination",destination_service_namespace="%s"} [%vs])) by (%s) %s`,
 			query = fmt.Sprintf(`sum(rate(%s{%s%s,destination_workload_namespace="%s"} [%vs])) by (%s) %s`,
@@ -262,8 +291,8 @@ func buildNamespaceTrafficMap(ctx context.Context, namespace string, o graph.Tel
 				int(duration.Seconds()), // range duration for the query
 				groupBy,
 				idleCondition)
-			incomingVector = promQuery(query, time.Unix(o.QueryTime, 0), promApi)
-			populateTrafficMap(trafficMap, &incomingVector, metric, o, globalInfo)
+			trafficVector = promQuery(query, time.Unix(o.QueryTime, 0), promApi)
+			populateTrafficMap(trafficMap, &trafficVector, metric, o, globalInfo)
 
 			// 2) Outgoing: query source telemetry to capture namespace workloads' outgoing traffic
 			query = fmt.Sprintf(`sum(rate(%s{%s%s,source_workload_namespace="%s"} [%vs])) by (%s) %s`,
@@ -274,8 +303,8 @@ func buildNamespaceTrafficMap(ctx context.Context, namespace string, o graph.Tel
 				int(duration.Seconds()), // range duration for the query
 				groupBy,
 				idleCondition)
-			outgoingVector := promQuery(query, time.Unix(o.QueryTime, 0), promApi)
-			populateTrafficMap(trafficMap, &outgoingVector, metric, o, globalInfo)
+			trafficVector = promQuery(query, time.Unix(o.QueryTime, 0), promApi)
+			populateTrafficMap(trafficMap, &trafficVector, metric, o, globalInfo)
 		}
 	}
 
@@ -512,11 +541,12 @@ func timeSeriesHash(cluster, serviceNs, service, workloadNs, workload, app, vers
 
 // BuildNodeTrafficMap is required by the graph/TelemtryVendor interface
 func BuildNodeTrafficMap(o graph.TelemetryOptions, globalInfo *graph.GlobalInfo) (graph.TrafficMap, error) {
+	namespace := o.NodeOptions.Namespace.Name
 	if o.NodeOptions.Aggregate != "" {
 		return handleAggregateNodeTrafficMap(o, globalInfo), nil
 	}
 
-	n, err := graph.NewNode(o.NodeOptions.Cluster, o.NodeOptions.Namespace, o.NodeOptions.Service, o.NodeOptions.Namespace, o.NodeOptions.Workload, o.NodeOptions.App, o.NodeOptions.Version, o.GraphType)
+	n, err := graph.NewNode(o.NodeOptions.Cluster, namespace, o.NodeOptions.Service, namespace, o.NodeOptions.Workload, o.NodeOptions.App, o.NodeOptions.Version, o.GraphType)
 	if err != nil {
 		log.Warningf("Skipping NodeTrafficMap (bad node), %s", err)
 		return nil, err
@@ -539,7 +569,7 @@ func BuildNodeTrafficMap(o graph.TelemetryOptions, globalInfo *graph.GlobalInfo)
 
 	trafficMap := buildNodeTrafficMap(o.Cluster, o.NodeOptions.Namespace, n, o, globalInfo)
 
-	namespaceInfo := graph.NewAppenderNamespaceInfo(o.NodeOptions.Namespace)
+	namespaceInfo := graph.NewAppenderNamespaceInfo(o.NodeOptions.Namespace.Name)
 
 	for _, a := range appenders {
 		appenderTimer := internalmetrics.GetGraphAppenderTimePrometheusTimer(a.Name())
@@ -562,8 +592,9 @@ func BuildNodeTrafficMap(o graph.TelemetryOptions, globalInfo *graph.GlobalInfo)
 // buildNodeTrafficMap returns a map of all nodes requesting or requested by the target node (key=id). Node graphs
 // are from the perspective of the node, as such we use destination telemetry for incoming traffic and source telemetry
 // for outgoing traffic.
-func buildNodeTrafficMap(cluster, namespace string, n *graph.Node, o graph.TelemetryOptions, globalInfo *graph.GlobalInfo) graph.TrafficMap {
+func buildNodeTrafficMap(cluster string, namespaceInfo graph.NamespaceInfo, n *graph.Node, o graph.TelemetryOptions, globalInfo *graph.GlobalInfo) graph.TrafficMap {
 	// create map to aggregate traffic by protocol and response code
+	namespace := namespaceInfo.Name
 	trafficMap := graph.NewTrafficMap()
 	duration := o.Namespaces[namespace].Duration
 	idleCondition := "> 0"
@@ -571,6 +602,8 @@ func buildNodeTrafficMap(cluster, namespace string, n *graph.Node, o graph.Telem
 		idleCondition = ""
 	}
 	promApi := globalInfo.PromClient.API()
+	var query string
+	var trafficVector model.Vector
 
 	// only narrow by cluster if it is set on the target node
 	var sourceCluster, destCluster string
@@ -584,14 +617,20 @@ func buildNodeTrafficMap(cluster, namespace string, n *graph.Node, o graph.Telem
 		metric := "istio_requests_total"
 		groupBy := "source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags"
 
-		// query prometheus for request traffic in two queries:
+		// query prometheus for request traffic in two parts:
 		// 1) query for incoming traffic
-		var query string
+		//    for ambient namespaces query source and dest, incoming gateway traffic is source-reported, and no destination proxy is reporting
+		var reporter string
+		if namespaceInfo.IsAmbient {
+			reporter = util.GetReporter("source|destination", o.Rates)
+		} else {
+			reporter = util.GetReporter("destination", o.Rates)
+		}
 		switch n.NodeType {
 		case graph.NodeTypeWorkload:
 			query = fmt.Sprintf(`sum(rate(%s{%s%s,destination_workload_namespace="%s",destination_workload="%s"} [%vs])) by (%s) %s`,
 				metric,
-				util.GetReporter("destination", o.Rates),
+				reporter,
 				destCluster,
 				namespace,
 				n.Workload,
@@ -602,7 +641,7 @@ func buildNodeTrafficMap(cluster, namespace string, n *graph.Node, o graph.Telem
 			if graph.IsOK(n.Version) {
 				query = fmt.Sprintf(`sum(rate(%s{%s%s,destination_service_namespace="%s",destination_canonical_service="%s",destination_canonical_revision="%s"} [%vs])) by (%s) %s`,
 					metric,
-					util.GetReporter("destination", o.Rates),
+					reporter,
 					destCluster,
 					namespace,
 					n.App,
@@ -613,7 +652,7 @@ func buildNodeTrafficMap(cluster, namespace string, n *graph.Node, o graph.Telem
 			} else {
 				query = fmt.Sprintf(`sum(rate(%s{%s%s,destination_service_namespace="%s",destination_canonical_service="%s"} [%vs])) by (%s) %s`,
 					metric,
-					util.GetReporter("destination", o.Rates),
+					reporter,
 					destCluster,
 					namespace,
 					n.App,
@@ -633,8 +672,8 @@ func buildNodeTrafficMap(cluster, namespace string, n *graph.Node, o graph.Telem
 				int(duration.Seconds()), // range duration for the query
 				groupBy,
 				idleCondition)
-			vector := promQuery(query, time.Unix(o.QueryTime, 0), promApi)
-			populateTrafficMap(trafficMap, &vector, metric, o, globalInfo)
+			trafficVector = promQuery(query, time.Unix(o.QueryTime, 0), promApi)
+			populateTrafficMap(trafficMap, &trafficVector, metric, o, globalInfo)
 
 			// 1.b) query dest telemetry for requests to the service, serviced by service workloads
 			query = fmt.Sprintf(`sum(rate(%s{%s%s,destination_service_namespace="%s",destination_service=~"^%s\\.%s\\..*$"} [%vs])) by (%s) %s`,
@@ -650,8 +689,8 @@ func buildNodeTrafficMap(cluster, namespace string, n *graph.Node, o graph.Telem
 		default:
 			graph.Error(fmt.Sprintf("NodeType [%s] not supported", n.NodeType))
 		}
-		inVector := promQuery(query, time.Unix(o.QueryTime, 0), promApi)
-		populateTrafficMap(trafficMap, &inVector, metric, o, globalInfo)
+		trafficVector = promQuery(query, time.Unix(o.QueryTime, 0), promApi)
+		populateTrafficMap(trafficMap, &trafficVector, metric, o, globalInfo)
 
 		// 2) query for outbound traffic
 		switch n.NodeType {
@@ -693,8 +732,8 @@ func buildNodeTrafficMap(cluster, namespace string, n *graph.Node, o graph.Telem
 		default:
 			graph.Error(fmt.Sprintf("NodeType [%s] not supported", n.NodeType))
 		}
-		outVector := promQuery(query, time.Unix(o.QueryTime, 0), promApi)
-		populateTrafficMap(trafficMap, &outVector, metric, o, globalInfo)
+		trafficVector = promQuery(query, time.Unix(o.QueryTime, 0), promApi)
+		populateTrafficMap(trafficMap, &trafficVector, metric, o, globalInfo)
 	}
 
 	// gRPC message traffic
@@ -714,8 +753,6 @@ func buildNodeTrafficMap(cluster, namespace string, n *graph.Node, o graph.Telem
 		}
 
 		for _, metric := range metrics {
-			var query string
-
 			switch n.NodeType {
 			case graph.NodeTypeWorkload:
 				query = fmt.Sprintf(`sum(rate(%s{%s%s,destination_workload_namespace="%s",destination_workload="%s"} [%vs])) by (%s) %s`,
@@ -765,8 +802,8 @@ func buildNodeTrafficMap(cluster, namespace string, n *graph.Node, o graph.Telem
 			default:
 				graph.Error(fmt.Sprintf("NodeType [%s] not supported", n.NodeType))
 			}
-			incomingVector := promQuery(query, time.Unix(o.QueryTime, 0), promApi)
-			populateTrafficMap(trafficMap, &incomingVector, metric, o, globalInfo)
+			trafficVector = promQuery(query, time.Unix(o.QueryTime, 0), promApi)
+			populateTrafficMap(trafficMap, &trafficVector, metric, o, globalInfo)
 
 			// 2) query for outbound traffic
 			switch n.NodeType {
@@ -808,8 +845,8 @@ func buildNodeTrafficMap(cluster, namespace string, n *graph.Node, o graph.Telem
 			default:
 				graph.Error(fmt.Sprintf("NodeType [%s] not supported", n.NodeType))
 			}
-			outgoingVector := promQuery(query, time.Unix(o.QueryTime, 0), promApi)
-			populateTrafficMap(trafficMap, &outgoingVector, metric, o, globalInfo)
+			trafficVector = promQuery(query, time.Unix(o.QueryTime, 0), promApi)
+			populateTrafficMap(trafficMap, &trafficVector, metric, o, globalInfo)
 		}
 	}
 
@@ -842,8 +879,6 @@ func buildNodeTrafficMap(cluster, namespace string, n *graph.Node, o graph.Telem
 		}
 
 		for _, metric := range metrics {
-			var query string
-
 			switch n.NodeType {
 			case graph.NodeTypeWorkload:
 				query = fmt.Sprintf(`sum(rate(%s{%s%s%s,destination_workload_namespace="%s",destination_workload="%s"} [%vs])) by (%s) %s`,
@@ -897,8 +932,8 @@ func buildNodeTrafficMap(cluster, namespace string, n *graph.Node, o graph.Telem
 			default:
 				graph.Error(fmt.Sprintf("NodeType [%s] not supported", n.NodeType))
 			}
-			incomingVector := promQuery(query, time.Unix(o.QueryTime, 0), promApi)
-			populateTrafficMap(trafficMap, &incomingVector, metric, o, globalInfo)
+			trafficVector = promQuery(query, time.Unix(o.QueryTime, 0), promApi)
+			populateTrafficMap(trafficMap, &trafficVector, metric, o, globalInfo)
 
 			// 2) query for outbound traffic
 			switch n.NodeType {
@@ -943,8 +978,8 @@ func buildNodeTrafficMap(cluster, namespace string, n *graph.Node, o graph.Telem
 			default:
 				graph.Error(fmt.Sprintf("NodeType [%s] not supported", n.NodeType))
 			}
-			outgoingVector := promQuery(query, time.Unix(o.QueryTime, 0), promApi)
-			populateTrafficMap(trafficMap, &outgoingVector, metric, o, globalInfo)
+			trafficVector = promQuery(query, time.Unix(o.QueryTime, 0), promApi)
+			populateTrafficMap(trafficMap, &trafficVector, metric, o, globalInfo)
 		}
 	}
 
@@ -952,7 +987,7 @@ func buildNodeTrafficMap(cluster, namespace string, n *graph.Node, o graph.Telem
 }
 
 func handleAggregateNodeTrafficMap(o graph.TelemetryOptions, globalInfo *graph.GlobalInfo) graph.TrafficMap {
-	n := graph.NewAggregateNode(o.NodeOptions.Cluster, o.NodeOptions.Namespace, o.NodeOptions.Aggregate, o.NodeOptions.AggregateValue, o.NodeOptions.Service, o.NodeOptions.App)
+	n := graph.NewAggregateNode(o.NodeOptions.Cluster, o.NodeOptions.Namespace.Name, o.NodeOptions.Aggregate, o.NodeOptions.AggregateValue, o.NodeOptions.Service, o.NodeOptions.App)
 
 	log.Tracef("Build graph for aggregate node [%+v]", n)
 
@@ -960,9 +995,9 @@ func handleAggregateNodeTrafficMap(o graph.TelemetryOptions, globalInfo *graph.G
 		o.Appenders.AppenderNames = append(o.Appenders.AppenderNames, appender.AggregateNodeAppenderName)
 	}
 	appenders, finalizers := appender.ParseAppenders(o)
-	trafficMap := buildAggregateNodeTrafficMap(o.NodeOptions.Namespace, n, o, globalInfo)
+	trafficMap := buildAggregateNodeTrafficMap(o.NodeOptions.Namespace.Name, n, o, globalInfo)
 
-	namespaceInfo := graph.NewAppenderNamespaceInfo(o.NodeOptions.Namespace)
+	namespaceInfo := graph.NewAppenderNamespaceInfo(o.NodeOptions.Namespace.Name)
 
 	for _, a := range appenders {
 		appenderTimer := internalmetrics.GetGraphAppenderTimePrometheusTimer(a.Name())
@@ -980,6 +1015,10 @@ func handleAggregateNodeTrafficMap(o graph.TelemetryOptions, globalInfo *graph.G
 
 // buildAggregateNodeTrafficMap returns a map of all incoming and outgoing traffic from the perspective of the aggregate. Aggregates
 // are always generated for serviced requests and therefore via destination telemetry.
+//
+// TODO: This *may* require an additional query to pick up incoming gateway traffic (source reported) for ambient namespaces (no dest
+// proxy reporting) but because it's unclear whether this is a used feature, or whether we really need to handle that use case, I'm
+// deferring. If necessary, see the incoming traffic handling in buildNamespacesTrafficMap.
 func buildAggregateNodeTrafficMap(namespace string, n graph.Node, o graph.TelemetryOptions, globalInfo *graph.GlobalInfo) graph.TrafficMap {
 	interval := o.Namespaces[namespace].Duration
 
diff --git a/graph/telemetry/istio/util/util.go b/graph/telemetry/istio/util/util.go
index a85b1bb5c..a9ddb7bbc 100644
--- a/graph/telemetry/istio/util/util.go
+++ b/graph/telemetry/istio/util/util.go
@@ -142,7 +142,7 @@ func AddQueryScope(query string) string {
 	return strings.ReplaceAll(query, "{", scope)
 }
 
-// GetReporter returns the "report=" prom query fragment based on whether the reporter must include waypoint traffic
+// GetReporter returns the "reporter=" prom query fragment based on whether the reporter must include waypoint traffic
 func GetReporter(reporter string, rates graph.RequestedRates) string {
 	if rates.Ambient == graph.AmbientTrafficWaypoint || rates.Ambient == graph.AmbientTrafficTotal {
 		return fmt.Sprintf(`reporter=~"%s|waypoint"`, reporter)
diff --git a/graph/types.go b/graph/types.go
index 1000b8241..5c116db2b 100644
--- a/graph/types.go
+++ b/graph/types.go
@@ -46,9 +46,10 @@ type Edge struct {
 }
 
 type NamespaceInfo struct {
-	Name     string
-	Duration time.Duration
-	IsIstio  bool
+	Name      string
+	Duration  time.Duration
+	IsAmbient bool
+	IsIstio   bool
 }
 
 type NamespaceInfoMap map[string]NamespaceInfo
diff --git a/hack/crc-openshift.sh b/hack/crc-openshift.sh
index 3f20e6560..203611ef7 100755
--- a/hack/crc-openshift.sh
+++ b/hack/crc-openshift.sh
@@ -243,7 +243,12 @@ print_all_service_endpoints() {
 
 exec_ssh() {
   local sshcmd=${1}
-  ssh -y -i ${CRC_ROOT_DIR}/machines/crc/id_ecdsa -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null core@$(${CRC_COMMAND} ip) ${sshcmd}
+  # when network-mode is "user", the port is 2222.
+  local port=22
+  if ${CRC_COMMAND} config get network-mode | grep -q ' user$'; then
+    port=2222
+  fi
+  ssh -y -p ${port} -i ${CRC_ROOT_DIR}/machines/crc/id_ed25519 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null core@$(${CRC_COMMAND} ip) ${sshcmd}
 }
 
 expose_cluster() {
@@ -504,10 +509,10 @@ SCRIPT_ROOT="$( cd "$(dirname "$0")" ; pwd -P )"
 cd ${SCRIPT_ROOT}
 
 # The default version of the crc tool to be downloaded
-DEFAULT_CRC_DOWNLOAD_VERSION="2.42.0"
+DEFAULT_CRC_DOWNLOAD_VERSION="2.45.0"
 
 # The default version of the crc bundle - this is typically the version included with the CRC download
-DEFAULT_CRC_LIBVIRT_DOWNLOAD_VERSION="4.17.0"
+DEFAULT_CRC_LIBVIRT_DOWNLOAD_VERSION="4.17.7"
 
 # The default virtual CPUs assigned to the CRC VM
 DEFAULT_CRC_CPUS="6"
@@ -524,6 +529,9 @@ DEFAULT_DOWNLOAD_BUNDLE="false"
 # Enables cluster monitoring in the CRC cluster
 DEFAULT_ENABLE_CLUSTER_MONITORING="false"
 
+# Enables network mode "user" which uses the local host networking
+DEFAULT_ENABLE_USER_NETWORK_MODE="false"
+
 # process command line args to override environment
 _CMD=""
 while [[ $# -gt 0 ]]; do
@@ -617,6 +625,10 @@ while [[ $# -gt 0 ]]; do
       ENABLE_CLUSTER_MONITORING="$2"
       shift;shift
       ;;
+    -eunm|--enable-user-network-mode)
+      ENABLE_USER_NETWORK_MODE="$2"
+      shift;shift
+      ;;
     -p|--pull-secret-file)
       PULL_SECRET_FILE="$2"
       shift;shift
@@ -669,6 +681,10 @@ Valid options:
       If true, the cluster will have monitoring enabled.
       Default: ${DEFAULT_ENABLE_CLUSTER_MONITORING}
       Used only for the 'start' command.
+  -eunm|--enable-user-network-mode (true|false)
+      If true, the network mode will be changed to "user" and will enable CRC to use the local host's networking.
+      Default: ${DEFAULT_ENABLE_USER_NETWORK_MODE}
+      Used only for the 'start' command.
   -h|--help : this message
   -p|--pull-secret-file <filename>
       Specifies the file containing your Image pull secret.
@@ -738,6 +754,7 @@ CRC_MACHINE_IMAGE="${CRC_ROOT_DIR}/machines/crc/crc"
 CRC_OC_BIN="${CRC_ROOT_DIR}/bin/oc/oc"
 DOWNLOAD_BUNDLE="${DOWNLOAD_BUNDLE:-${DEFAULT_DOWNLOAD_BUNDLE}}"
 ENABLE_CLUSTER_MONITORING="${ENABLE_CLUSTER_MONITORING:-${DEFAULT_ENABLE_CLUSTER_MONITORING}}"
+ENABLE_USER_NETWORK_MODE="${ENABLE_USER_NETWORK_MODE:-${DEFAULT_ENABLE_USER_NETWORK_MODE}}"
 
 # VM configuration
 CRC_CPUS=${CRC_CPUS:-${DEFAULT_CRC_CPUS}}
@@ -795,6 +812,7 @@ debug "ENVIRONMENT:
   CRC_ROOT_DIR=$CRC_ROOT_DIR
   CRC_VIRTUAL_DISK_SIZE=$CRC_VIRTUAL_DISK_SIZE
   ENABLE_CLUSTER_MONITORING=$ENABLE_CLUSTER_MONITORING
+  ENABLE_USER_NETWORK_MODE=$ENABLE_USER_NETWORK_MODE
   OPENSHIFT_BIN_PATH=$OPENSHIFT_BIN_PATH
   "
 
@@ -895,8 +913,14 @@ if [ "$_CMD" = "start" ]; then
   fi
 
   # Unsure if we need these
-  #${CRC_COMMAND} config set network-mode user
-  #${CRC_COMMAND} config set host-network-access true
+  if [ "${ENABLE_USER_NETWORK_MODE}" == "true" ]; then
+    ${CRC_COMMAND} config set network-mode user
+    ${CRC_COMMAND} config set host-network-access true
+  else
+    if ${CRC_COMMAND} config get network-mode | grep -q ' user$'; then
+      infomsg "WARNING: --enable-user-network-mode is false but the current CRC config has it enabled."
+    fi
+  fi
 
   infomsg "Setting up the requirements for the OpenShift cluster..."
   debug "${CRC_COMMAND} setup"
diff --git a/hack/istio/install-bookinfo-demo.sh b/hack/istio/install-bookinfo-demo.sh
index 49b7308f7..64a4d133e 100755
--- a/hack/istio/install-bookinfo-demo.sh
+++ b/hack/istio/install-bookinfo-demo.sh
@@ -401,19 +401,22 @@ if [ "${TRAFFIC_GENERATOR_ENABLED}" == "true" ]; then
   echo "Installing Traffic Generator"
   if [ "${IS_OPENSHIFT}" == "true" ]; then
     echo "Determining the route to send traffic to, trying istio-ingressgateway route in ${INGRESS_NAMESPACE} namespace"
-    # first, try istio-ingressgateway in istio-system, wait for a while the host is populated
+
+    # first, try route for gateway in bookinfo namespace created by gateway-api
     # make sure you have latest kubectl/oc which support JSONPath condition without value
-    ${CLIENT_EXE} wait --for=jsonpath='{.status.ingress[].host}' --timeout=10s route istio-ingressgateway -n ${INGRESS_NAMESPACE}
-    INGRESS_ROUTE=$(${CLIENT_EXE} get route istio-ingressgateway -o jsonpath='{.spec.host}{"\n"}' -n ${INGRESS_NAMESPACE})
+    ${CLIENT_EXE} wait --for=jsonpath='{.status.ingress[].host}' --timeout=10s route bookinfo-gateway-istio -n ${NAMESPACE}
+    INGRESS_ROUTE=$(${CLIENT_EXE} get route bookinfo-gateway-istio -o jsonpath='{.spec.host}{"\n"}' -n ${NAMESPACE})
     if [ -z "${INGRESS_ROUTE}" ]; then
       sleep 1
-      echo "No istio-ingressgateway route in ${INGRESS_NAMESPACE} namespace, next, trying bookinfo-gateway-istio route in ${NAMESPACE} namespace"
-      # next, try route for gateway in bookinfo namespace created by gateway-api
-      ${CLIENT_EXE} wait --for=jsonpath='{.status.ingress[].host}' --timeout=10s route bookinfo-gateway-istio -n ${NAMESPACE}
-      INGRESS_ROUTE=$(${CLIENT_EXE} get route bookinfo-gateway-istio -o jsonpath='{.spec.host}{"\n"}' -n ${NAMESPACE})
+      echo "No bookinfo-gateway-istio route in ${NAMESPACE} namespace, next, trying istio-ingressgateway route in ${INGRESS_NAMESPACE} namespace"
+
+      # next, try istio-ingressgateway in istio-system, wait for a while the host is populated
+      ${CLIENT_EXE} wait --for=jsonpath='{.status.ingress[].host}' --timeout=10s route istio-ingressgateway -n ${INGRESS_NAMESPACE}
+      INGRESS_ROUTE=$(${CLIENT_EXE} get route istio-ingressgateway -o jsonpath='{.spec.host}{"\n"}' -n ${INGRESS_NAMESPACE})
       if [ -z "${INGRESS_ROUTE}" ]; then
         sleep 1
-        echo "No bookinfo-gateway-istio route in ${NAMESPACE} namespace, the route for productpage app will be used dirrectly"
+        echo "No istio-ingressgateway route in ${INGRESS_NAMESPACE} namespace, the route for productpage app will be used dirrectly"
+
         # nevermind, use productpage route directly
         ${CLIENT_EXE} wait --for=jsonpath='{.status.ingress[].host}' --timeout=10s route productpage -n ${NAMESPACE}
         INGRESS_ROUTE=$(${CLIENT_EXE} get route productpage -o jsonpath='{.spec.host}{"\n"}' -n ${NAMESPACE})
diff --git a/hack/istio/install-istio-via-sail.sh b/hack/istio/install-istio-via-sail.sh
index b1d8d7865..1fe4c64b4 100755
--- a/hack/istio/install-istio-via-sail.sh
+++ b/hack/istio/install-istio-via-sail.sh
@@ -72,7 +72,7 @@ kind: Istio
 metadata:
   name: default
 spec:
-  version: v1.23.0
+  version: v1.24.1
   namespace: istio-system
   updateStrategy:
     type: InPlace
diff --git a/hack/use-openshift-prometheus.sh b/hack/use-openshift-prometheus.sh
index dfd32aa9c..c2f93467c 100755
--- a/hack/use-openshift-prometheus.sh
+++ b/hack/use-openshift-prometheus.sh
@@ -16,6 +16,7 @@ KIALI_CR_NAMESPACE=""
 MESH_LABEL="mymesh"
 NAMESPACES=""
 NETWORK_POLICIES="false"
+PROM_INSECURE="false"
 OC="oc"
 
 # process command line args
@@ -29,6 +30,7 @@ while [[ $# -gt 0 ]]; do
     -n|--namespaces)            NAMESPACES="$2"        ; shift;shift ;;
     -np|--network-policies)     NETWORK_POLICIES="$2"  ; shift;shift ;;
     -oc|--oc)                   OC="$2"                ; shift;shift ;;
+    -pi|--prometheus-insecure)  PROM_INSECURE="$2"     ; shift;shift ;;
     -h|--help)
       cat <<HELPMSG
 Valid command line arguments:
@@ -39,6 +41,7 @@ Valid command line arguments:
   -n|--namespaces <names>: Space-separated names of namespaces in the mesh (Default: empty)
   -np|--network-policies (true|false) If true, NetworkPolicies will be created (or deleted if --delete is true) to allow for all ingress traffic, including from OpenShift monitoring namespaces labeled with network.openshift.io/policy-group: monitoring (where Prometheus lives) (Default: false)
   -oc|--oc <path>: Cluster client executable name of 'oc' (Default: oc)
+  -pi|--prometheus-insecure (true|false): If Prometheus server certificate is self-signed, you need to set this to "true" to tell Kiali to connect to Prometheus insecurely. (Default: false)
   -h|--help : this message
 HELPMSG
       exit 1
@@ -336,6 +339,7 @@ spec:
   external_services:
     prometheus:
       auth:
+        insecure_skip_verify: ${PROM_INSECURE}
         type: bearer
         use_kiali_token: true
       query_scope:
@@ -345,7 +349,7 @@ spec:
       url: https://thanos-querier.openshift-monitoring.svc.cluster.local:9091
 EOM
   echo "..."
-  ${OC} patch kiali ${KIALI_CR_NAME} -n ${KIALI_CR_NAMESPACE} --type=merge --patch '{"spec":{"external_services":{"prometheus":{"auth":{"type":"bearer","use_kiali_token": true},"query_scope":{"mesh_id": "'${MESH_LABEL}'"},"thanos_proxy":{"enabled": true},"url":"https://thanos-querier.openshift-monitoring.svc.cluster.local:9091"}}}}'
+  ${OC} patch kiali ${KIALI_CR_NAME} -n ${KIALI_CR_NAMESPACE} --type=merge --patch '{"spec":{"external_services":{"prometheus":{"auth":{"insecure_skip_verify": '${PROM_INSECURE}',"type":"bearer","use_kiali_token": true},"query_scope":{"mesh_id": "'${MESH_LABEL}'"},"thanos_proxy":{"enabled": true},"url":"https://thanos-querier.openshift-monitoring.svc.cluster.local:9091"}}}}'
 }
 
 ########## MAIN
diff --git a/handlers/workloads.go b/handlers/workloads.go
index 98b42412c..8d79f3661 100644
--- a/handlers/workloads.go
+++ b/handlers/workloads.go
@@ -320,3 +320,25 @@ func PodLogs(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 }
+
+func ConfigDumpZtunnel(w http.ResponseWriter, r *http.Request) {
+	params := mux.Vars(r)
+
+	business, err := getBusiness(r)
+	if err != nil {
+		RespondWithError(w, http.StatusInternalServerError, "Services initialization error: "+err.Error())
+		return
+	}
+
+	cluster := clusterNameFromQuery(r.URL.Query())
+	namespace := params["namespace"]
+	pod := params["pod"]
+
+	dump := business.Workload.GetZtunnelConfig(cluster, namespace, pod)
+	if err != nil {
+		handleErrorResponse(w, err)
+		return
+	}
+
+	RespondWithJSON(w, http.StatusOK, dump)
+}
diff --git a/istio/discovery.go b/istio/discovery.go
index 7ec1130fc..5400160eb 100644
--- a/istio/discovery.go
+++ b/istio/discovery.go
@@ -100,6 +100,33 @@ func (in *Discovery) getControlPlaneConfiguration(kubeCache cache.KubeCache, con
 		istioConfigMapInfo.Certificates = append(istioConfigMapInfo.Certificates, cert)
 	}
 
+	// Check ztunnel pods to get the config dump
+	if in.kialiCache.IsAmbientEnabled(controlPlane.Cluster.Name) {
+		ztunnelPods := in.kialiCache.GetZtunnelPods(controlPlane.Cluster.Name)
+		if len(ztunnelPods) > 0 {
+			client := in.kialiSAClients[controlPlane.Cluster.Name]
+			zTunnel := make(map[string]*kubernetes.ZtunnelConfigDump)
+
+			for _, zPod := range ztunnelPods {
+				resp, err := client.ForwardGetRequest(zPod.Namespace, zPod.Name, 15000, "/config_dump")
+				if err != nil {
+					log.Errorf("[getZtunnelConfigDump] Error forwarding the /config_dump request: %v", err)
+					return nil, err
+				}
+
+				configDump := &kubernetes.ZtunnelConfigDump{}
+				err = json.Unmarshal(resp, configDump)
+				if err != nil {
+					log.Errorf("[getZtunnelConfigDump] Error Unmarshalling the config_dump: %v", err)
+				} else {
+					key := fmt.Sprintf("%s%s%s", client.ClusterInfo().Name, zPod.Namespace, zPod.Name)
+					zTunnel[key] = configDump
+				}
+			}
+			in.kialiCache.SetZtunnelDump(zTunnel)
+		}
+	}
+
 	return &models.ControlPlaneConfiguration{
 		IstioMeshConfig: *istioConfigMapInfo,
 		Network:         in.resolveNetwork(kubeCache, controlPlane),
diff --git a/kubernetes/cache/cache.go b/kubernetes/cache/cache.go
index b4e542619..d4b78525e 100644
--- a/kubernetes/cache/cache.go
+++ b/kubernetes/cache/cache.go
@@ -78,6 +78,7 @@ type KialiCache interface {
 
 	RegistryStatusCache
 	ProxyStatusCache
+	ZtunnelDumpCache
 
 	// SetValidations caches validations for a cluster/namespace.
 	Validations() store.Store[models.IstioValidationKey, *models.IstioValidation]
@@ -126,6 +127,8 @@ type kialiCacheImpl struct {
 	proxyStatusStore store.Store[string, *kubernetes.ProxyStatus]
 	// RegistryStatusStore stores the registry status and should be key'd off of the cluster name.
 	registryStatusStore store.Store[string, *kubernetes.RegistryStatus]
+	// ProxyStatusStore stores ztunnel config dump per cluster + namespace + pod.
+	ztunnelConfigStore store.Store[string, *kubernetes.ZtunnelConfigDump]
 
 	waypointList models.WaypointStore
 	// validations key'd by the validation key
@@ -151,6 +154,7 @@ func NewKialiCache(kialiSAClients map[string]kubernetes.ClientInterface, cfg con
 		refreshDuration:         time.Duration(cfg.KubernetesConfig.CacheDuration) * time.Second,
 		proxyStatusStore:        store.New[string, *kubernetes.ProxyStatus](),
 		registryStatusStore:     store.New[string, *kubernetes.RegistryStatus](),
+		ztunnelConfigStore:      store.New[string, *kubernetes.ZtunnelConfigDump](),
 	}
 
 	for cluster, client := range kialiSAClients {
diff --git a/kubernetes/cache/kube_cache.go b/kubernetes/cache/kube_cache.go
index 2255ff7fa..23b55e6e2 100644
--- a/kubernetes/cache/kube_cache.go
+++ b/kubernetes/cache/kube_cache.go
@@ -532,6 +532,8 @@ func (c *kubeCache) createKubernetesInformers(namespace string) informers.Shared
 						Type:         obj.Spec.Type,
 						ExternalName: obj.Spec.ExternalName,
 						ClusterIP:    obj.Spec.ClusterIP,
+						ClusterIPs:   obj.Spec.ClusterIPs,
+						IPFamilies:   obj.Spec.IPFamilies,
 					},
 				}
 				return trimmedService, nil
diff --git a/kubernetes/cache/ztunnel_dump.go b/kubernetes/cache/ztunnel_dump.go
new file mode 100644
index 000000000..1cb181a16
--- /dev/null
+++ b/kubernetes/cache/ztunnel_dump.go
@@ -0,0 +1,27 @@
+package cache
+
+import (
+	"github.com/kiali/kiali/kubernetes"
+)
+
+func ztunnelDumpKey(cluster, namespace, pod string) string {
+	return cluster + namespace + pod
+}
+
+type ZtunnelDumpCache interface {
+	SetZtunnelDump(ztunnelConfig map[string]*kubernetes.ZtunnelConfigDump)
+	GetZtunnelDump(cluster, namespace, pod string) *kubernetes.ZtunnelConfigDump
+}
+
+func (c *kialiCacheImpl) SetZtunnelDump(ztunnelConfig map[string]*kubernetes.ZtunnelConfigDump) {
+	c.ztunnelConfigStore.Replace(ztunnelConfig)
+}
+
+func (c *kialiCacheImpl) GetZtunnelDump(cluster, namespace, pod string) *kubernetes.ZtunnelConfigDump {
+	key := ztunnelDumpKey(cluster, namespace, pod)
+	config, found := c.ztunnelConfigStore.Get(key)
+	if !found {
+		return nil
+	}
+	return config
+}
diff --git a/kubernetes/istio.go b/kubernetes/istio.go
index 4e50e17ee..12c0411e4 100644
--- a/kubernetes/istio.go
+++ b/kubernetes/istio.go
@@ -80,6 +80,7 @@ type IstioClientInterface interface {
 	GatewayAPI() gatewayapiclient.Interface
 
 	GetConfigDump(namespace, podName string) (*ConfigDump, error)
+	GetZtunnelConfigDump(namespace, podName string) (*ZtunnelConfigDump, error)
 	SetProxyLogLevel(namespace, podName, level string) error
 }
 
@@ -112,6 +113,25 @@ func (in *K8SClient) GetConfigDump(namespace, podName string) (*ConfigDump, erro
 	return cd, err
 }
 
+func (in *K8SClient) GetZtunnelConfigDump(namespace, podName string) (*ZtunnelConfigDump, error) {
+	// Fetching the Config Dump from the pod's ztunnel.
+	// The port 15000 is open on each ztunnel pod (managed by Istio)
+	// And returns different data than the Envoy Proxy
+	resp, err := in.ForwardGetRequest(namespace, podName, 15000, "/config_dump")
+	if err != nil {
+		log.Errorf("Error forwarding the /config_dump request: %s", err.Error())
+		return nil, err
+	}
+
+	cd := &ZtunnelConfigDump{}
+	err = json.Unmarshal(resp, cd)
+	if err != nil {
+		log.Errorf("Error Unmarshalling the ztunnel config_dump: %s", err.Error())
+	}
+
+	return cd, err
+}
+
 func (in *K8SClient) SetProxyLogLevel(namespace, pod, level string) error {
 	path := fmt.Sprintf("/logging?level=%s", level)
 
diff --git a/kubernetes/ztunnel.go b/kubernetes/ztunnel.go
new file mode 100644
index 000000000..ddae108c5
--- /dev/null
+++ b/kubernetes/ztunnel.go
@@ -0,0 +1,164 @@
+package kubernetes
+
+type ZtunnelConfigDump struct {
+	Certificates []Certificate `json:"certificates"`
+	Config       Config        `json:"config"`
+	Policies     []interface{} `json:"policies"`
+	Services     []Service     `json:"services"`
+	Workloads    []Workload    `json:"workloads"`
+}
+
+type Certificate struct {
+	CertChain []CertChain `json:"certChain"`
+	Identity  string      `json:"identity"`
+	State     string      `json:"state"`
+}
+
+type CertChain struct {
+	ExpirationTime string `json:"expirationTime"`
+	Pem            string `json:"pem"`
+	SerialNumber   string `json:"serialNumber,omitempty"`
+	ValidFrom      string `json:"validFrom"`
+}
+
+type Config struct {
+	AdminAddr                Address            `json:"adminAddr"`
+	AltCaHostname            *string            `json:"altCaHostname"`
+	AltXdsHostname           *string            `json:"altXdsHostname"`
+	CaAddress                string             `json:"caAddress"`
+	CaRootCert               FilePath           `json:"caRootCert"`
+	ClusterDomain            string             `json:"clusterDomain"`
+	ClusterID                string             `json:"clusterId"`
+	ConnectionWindowSize     int                `json:"connectionWindowSize"`
+	DNSProxy                 bool               `json:"dnsProxy"`
+	DNSProxyAddr             Address            `json:"dnsProxyAddr"`
+	DNSResolverCfg           DNSResolverConfig  `json:"dnsResolverCfg"`
+	DNSResolverOpts          DNSResolverOptions `json:"dnsResolverOpts"`
+	FakeCa                   bool               `json:"fakeCa"`
+	FakeSelfInbound          bool               `json:"fakeSelfInbound"`
+	FrameSize                int                `json:"frameSize"`
+	IllegalPorts             []int              `json:"illegalPorts"`
+	InboundAddr              string             `json:"inboundAddr"`
+	InboundPlaintextAddr     string             `json:"inboundPlaintextAddr"`
+	InPodPortReuse           bool               `json:"inpodPortReuse"`
+	InPodUds                 string             `json:"inpodUds"`
+	LocalNode                string             `json:"localNode"`
+	Network                  string             `json:"network"`
+	NumWorkerThreads         int                `json:"numWorkerThreads"`
+	OutboundAddr             string             `json:"outboundAddr"`
+	PacketMark               int                `json:"packetMark"`
+	PoolMaxStreamsPerConn    int                `json:"poolMaxStreamsPerConn"`
+	PoolUnusedReleaseTimeout TimeDuration       `json:"poolUnusedReleaseTimeout"`
+	Proxy                    bool               `json:"proxy"`
+	ProxyArgs                string             `json:"proxyArgs"`
+	ProxyMetadata            map[string]string  `json:"proxyMetadata"`
+	ProxyMode                string             `json:"proxyMode"`
+	ProxyWorkloadInfo        *string            `json:"proxyWorkloadInformation"`
+	ReadinessAddr            SocketAddress      `json:"readinessAddr"`
+	RequireOriginalSource    *string            `json:"requireOriginalSource"`
+	SecretTtl                TimeDuration       `json:"secretTtl"`
+	SelfTerminationDeadline  TimeDuration       `json:"selfTerminationDeadline"`
+	Socks5Addr               *string            `json:"socks5Addr"`
+	StatsAddr                SocketAddress      `json:"statsAddr"`
+	WindowSize               int                `json:"windowSize"`
+	XdsAddress               string             `json:"xdsAddress"`
+	XdsOnDemand              bool               `json:"xdsOnDemand"`
+	XdsRootCert              FilePath           `json:"xdsRootCert"`
+}
+
+type Address struct {
+	Localhost []interface{} `json:"Localhost"`
+}
+
+type FilePath struct {
+	File string `json:"File"`
+}
+
+type DNSResolverConfig struct {
+	Domain      *string      `json:"domain"`
+	NameServers []NameServer `json:"name_servers"`
+	Search      []string     `json:"search"`
+}
+
+type NameServer struct {
+	BindAddr               *string `json:"bind_addr"`
+	Protocol               string  `json:"protocol"`
+	SocketAddr             string  `json:"socket_addr"`
+	TlsDnsName             *string `json:"tls_dns_name"`
+	TrustNegativeResponses bool    `json:"trust_negative_responses"`
+}
+
+type DNSResolverOptions struct {
+	Attempts               int          `json:"attempts"`
+	AuthenticData          bool         `json:"authentic_data"`
+	CacheSize              int          `json:"cache_size"`
+	CheckNames             bool         `json:"check_names"`
+	Edns0                  bool         `json:"edns0"`
+	IpStrategy             string       `json:"ip_strategy"`
+	Ndots                  int          `json:"ndots"`
+	NegativeMaxTtl         *string      `json:"negative_max_ttl"`
+	NegativeMinTtl         *string      `json:"negative_min_ttl"`
+	NumConcurrentReqs      int          `json:"num_concurrent_reqs"`
+	PositiveMaxTtl         *string      `json:"positive_max_ttl"`
+	PositiveMinTtl         *string      `json:"positive_min_ttl"`
+	PreserveIntermediates  bool         `json:"preserve_intermediates"`
+	RecursionDesired       bool         `json:"recursion_desired"`
+	Rotate                 bool         `json:"rotate"`
+	ServerOrderingStrategy string       `json:"server_ordering_strategy"`
+	ShuffleDnsServers      bool         `json:"shuffle_dns_servers"`
+	Timeout                TimeDuration `json:"timeout"`
+	TryTcpOnError          bool         `json:"try_tcp_on_error"`
+	UseHostsFile           bool         `json:"use_hosts_file"`
+	Validate               bool         `json:"validate"`
+}
+
+type TimeDuration struct {
+	Nanos int `json:"nanos"`
+	Secs  int `json:"secs"`
+}
+
+type SocketAddress struct {
+	SocketAddr string `json:"SocketAddr"`
+}
+
+type Service struct {
+	Endpoints       map[string]Endpoint `json:"endpoints"`
+	Hostname        string              `json:"hostname"`
+	IpFamilies      string              `json:"ipFamilies"`
+	Name            string              `json:"name"`
+	Namespace       string              `json:"namespace"`
+	Ports           map[string]int      `json:"ports"`
+	SubjectAltNames []string            `json:"subjectAltNames"`
+	Vips            []string            `json:"vips"`
+	Waypoint        Waypoint            `json:"waypoint"`
+}
+
+type Endpoint struct {
+	Port        map[string]int `json:"port"`
+	Status      string         `json:"status"`
+	WorkloadUid string         `json:"workloadUid"`
+}
+
+type Waypoint struct {
+	Destination   string `json:"destination"`
+	HboneMtlsPort int    `json:"hboneMtlsPort"`
+}
+
+type Workload struct {
+	CanonicalName     string   `json:"canonicalName"`
+	CanonicalRevision string   `json:"canonicalRevision"`
+	ClusterID         string   `json:"clusterId"`
+	Name              string   `json:"name"`
+	Namespace         string   `json:"namespace"`
+	NetworkMode       string   `json:"networkMode"`
+	Node              string   `json:"node"`
+	Protocol          string   `json:"protocol"`
+	ServiceAccount    string   `json:"serviceAccount"`
+	Services          []string `json:"services"`
+	Status            string   `json:"status"`
+	TrustDomain       string   `json:"trustDomain"`
+	Uid               string   `json:"uid"`
+	WorkloadIps       []string `json:"workloadIps"`
+	WorkloadName      string   `json:"workloadName"`
+	WorkloadType      string   `json:"workloadType"`
+}
diff --git a/models/pod.go b/models/pod.go
index bb4d8e43b..91a7e830c 100644
--- a/models/pod.go
+++ b/models/pod.go
@@ -13,9 +13,7 @@ import (
 type Pods []*Pod
 
 const (
-	AmbientAnnotation        = "ambient.istio.io/redirection"
-	AmbientAnnotationEnabled = "enabled"
-	IstioProxy               = "istio-proxy"
+	IstioProxy = "istio-proxy"
 )
 
 // Pod holds a subset of v1.Pod data that is meaningful in Kiali
@@ -33,6 +31,7 @@ type Pod struct {
 	AppLabel            bool              `json:"appLabel"`
 	VersionLabel        bool              `json:"versionLabel"`
 	Annotations         map[string]string `json:"annotations"`
+	Protocol            string            `json:"protocol"`
 	ProxyStatus         *ProxyStatus      `json:"proxyStatus"`
 	ServiceAccountName  string            `json:"serviceAccountName"`
 }
@@ -221,11 +220,6 @@ func (pod *Pod) AmbientEnabled() bool {
 	return pod.Annotations[config.AmbientAnnotation] == config.AmbientAnnotationEnabled
 }
 
-// IsWaypoint returns true if the pod is a waypoint proxy
-func (pod *Pod) IsWaypoint() bool {
-	return config.IsWaypoint(pod.Labels)
-}
-
 // HasNativeSidecar returns true if the pod has istio-proxy init containers
 func (pod *Pod) HasNativeSidecar() bool {
 	for _, c := range pod.IstioInitContainers {
diff --git a/models/service.go b/models/service.go
index 0fcdf4292..2e344bf91 100644
--- a/models/service.go
+++ b/models/service.go
@@ -106,20 +106,22 @@ type ServiceDetails struct {
 type (
 	Services []*Service
 	Service  struct {
-		AdditionalDetails []AdditionalItem  `json:"additionalDetails"`
-		Annotations       map[string]string `json:"annotations"`
-		Cluster           string            `json:"cluster"`
-		CreatedAt         string            `json:"createdAt"`
-		ExternalName      string            `json:"externalName"`
-		HealthAnnotations map[string]string `json:"healthAnnotations"`
-		Ip                string            `json:"ip"`
-		Labels            map[string]string `json:"labels"`
-		Name              string            `json:"name"`
-		Namespace         string            `json:"namespace"`
-		Ports             Ports             `json:"ports"`
-		ResourceVersion   string            `json:"resourceVersion"`
-		Selectors         map[string]string `json:"selectors"`
-		Type              string            `json:"type"`
+		AdditionalDetails []AdditionalItem   `json:"additionalDetails"`
+		Annotations       map[string]string  `json:"annotations"`
+		Cluster           string             `json:"cluster"`
+		CreatedAt         string             `json:"createdAt"`
+		ExternalName      string             `json:"externalName"`
+		HealthAnnotations map[string]string  `json:"healthAnnotations"`
+		Ip                string             `json:"ip"`
+		Ips               []string           `json:"ips,omitempty"`
+		IpFamilies        []core_v1.IPFamily `json:"ipFamilies,omitempty"`
+		Labels            map[string]string  `json:"labels"`
+		Name              string             `json:"name"`
+		Namespace         string             `json:"namespace"`
+		Ports             Ports              `json:"ports"`
+		ResourceVersion   string             `json:"resourceVersion"`
+		Selectors         map[string]string  `json:"selectors"`
+		Type              string             `json:"type"`
 	}
 )
 
@@ -157,6 +159,8 @@ func (s *Service) Parse(cluster string, service *core_v1.Service) {
 		s.ExternalName = service.Spec.ExternalName
 		s.HealthAnnotations = GetHealthAnnotation(service.Annotations, GetHealthConfigAnnotation())
 		s.Ip = service.Spec.ClusterIP
+		s.Ips = service.Spec.ClusterIPs
+		s.IpFamilies = service.Spec.IPFamilies
 		s.Labels = service.Labels
 		s.Name = service.Name
 		s.Namespace = service.Namespace
diff --git a/models/workload.go b/models/workload.go
index a399037a6..62e8241f0 100644
--- a/models/workload.go
+++ b/models/workload.go
@@ -66,6 +66,11 @@ type WorkloadListItem struct {
 	// Namespace of the workload
 	Namespace string `json:"namespace"`
 
+	// If is part of the Ambient infrastructure
+	// required: false
+	// example: waypoint/ztunnel
+	Ambient string `json:"ambient"`
+
 	// The kube cluster where this workload is located.
 	Cluster string `json:"cluster"`
 
@@ -226,6 +231,9 @@ func (workload *WorkloadListItem) ParseWorkload(w *Workload) {
 	}
 	workload.HealthAnnotations = w.HealthAnnotations
 	workload.IstioReferences = []*IstioValidationKey{}
+	if w.IsWaypoint() {
+		workload.Ambient = "waypoint"
+	}
 
 	/** Check the labels app and version required by Istio in template Pods*/
 	_, workload.AppLabel = w.Labels[conf.IstioLabels.AppLabelName]
@@ -467,6 +475,18 @@ func (workload *Workload) SetPods(pods []core_v1.Pod) {
 	workload.IsAmbient = workload.HasIstioAmbient()
 }
 
+func (workload *Workload) AddPodsProtocol(ztunnelConfig kubernetes.ZtunnelConfigDump) {
+
+	for _, pod := range workload.Pods {
+		for _, wk := range ztunnelConfig.Workloads {
+			if wk.Name == pod.Name {
+				pod.Protocol = wk.Protocol
+				break
+			}
+		}
+	}
+}
+
 func (workload *Workload) SetServices(svcs *ServiceList) {
 	workload.Services = svcs.Services
 }
@@ -486,6 +506,7 @@ func (workload *Workload) HasIstioSidecar() bool {
 }
 
 // IsGateway return true if the workload is Ingress, Egress or K8s Gateway
+// waypoint proxies are not included. Use IsWaypoint() instead
 func (workload *Workload) IsGateway() bool {
 	// There's not consistent labeling for gateways.
 	// In case of using istioctl, you get:
@@ -540,6 +561,22 @@ func (workload *Workload) IsGateway() bool {
 	return false
 }
 
+// IsWaypoint return true if the workload is a waypoint proxy (Based in labels)
+func (workload *Workload) IsWaypoint() bool {
+
+	return workload.Labels["gateway.istio.io/managed"] == "istio.io-mesh-controller"
+}
+
+// IsWaypoint return true if the workload is a ztunnel (Based in labels)
+func (workload *Workload) IsZtunnel() bool {
+	for _, pod := range workload.Pods {
+		if pod.Labels["app"] == "ztunnel" {
+			return true
+		}
+	}
+	return false
+}
+
 // HasIstioAmbient returns true if the workload has any pod with Ambient mesh annotations
 func (workload *Workload) HasIstioAmbient() bool {
 	// if no pods we can't prove that ambient is enabled, so return false (Default)
diff --git a/routing/routes.go b/routing/routes.go
index 4d7f5b573..7d20b4d07 100644
--- a/routing/routes.go
+++ b/routing/routes.go
@@ -1442,6 +1442,27 @@ func NewRoutes(
 			handlers.ConfigDumpResourceEntries,
 			true,
 		},
+		// swagger:route GET /api/namespaces/{namespace}/pods/{pod}/config_dump_ztunnel
+		// ---
+		// Endpoint to get ztunnel pod config dump
+		//
+		//     Produces:
+		//     - application/json
+		//
+		//     Schemes: http, https
+		//
+		// responses:
+		//      500: internalError
+		//      404: notFoundError
+		//      200: ZtunnelConfigDump
+		//
+		{
+			"ZtunnelConfigDump",
+			"GET",
+			"/api/namespaces/{namespace}/pods/{pod}/config_dump_ztunnel",
+			handlers.ConfigDumpZtunnel,
+			true,
+		},
 		// swagger:route POST /namespaces/{namespace}/pods/{pod}/logging pods podProxyLogging
 		// ---
 		// Endpoint to set pod proxy log level


Test patch:
diff --git a/business/checkers/virtualservices/route_checker_test.go b/business/checkers/virtualservices/route_checker_test.go
index 088559690..c4f98819c 100644
--- a/business/checkers/virtualservices/route_checker_test.go
+++ b/business/checkers/virtualservices/route_checker_test.go
@@ -42,6 +42,61 @@ func TestServiceMultipleChecks(t *testing.T) {
 	assert.NoError(validations.ConfirmIstioCheckMessage("virtualservices.route.singleweight", vals[0]))
 	assert.Equal(vals[0].Severity, models.WarningSeverity)
 	assert.Equal(vals[0].Path, "spec/http[0]/route[0]/weight")
+
+	vals, valid = RouteChecker{
+		Namespaces:     []string{"test"},
+		VirtualService: fakeOneTcpRouteUnder100(),
+	}.Check()
+
+	// wrong weight'ed route rule
+	assert.True(valid)
+	assert.NotEmpty(vals)
+	assert.Len(vals, 1)
+	assert.NoError(validations.ConfirmIstioCheckMessage("virtualservices.route.singleweight", vals[0]))
+	assert.Equal(vals[0].Severity, models.WarningSeverity)
+	assert.Equal(vals[0].Path, "spec/tcp[0]/route[0]/weight")
+
+	vals, valid = RouteChecker{
+		Namespaces:     []string{"test"},
+		VirtualService: fakeOneTlsRouteUnder100(),
+	}.Check()
+
+	// wrong weight'ed route rule
+	assert.True(valid)
+	assert.NotEmpty(vals)
+	assert.Len(vals, 1)
+	assert.NoError(validations.ConfirmIstioCheckMessage("virtualservices.route.singleweight", vals[0]))
+	assert.Equal(vals[0].Severity, models.WarningSeverity)
+	assert.Equal(vals[0].Path, "spec/tls[0]/route[0]/weight")
+}
+
+// VirtualService with one route and no weight
+func TestServiceEmptyWeight(t *testing.T) {
+	assert := assert.New(t)
+
+	vals, valid := RouteChecker{
+		Namespaces:     []string{"test"},
+		VirtualService: fakeOneRouteNoWeight(),
+	}.Check()
+
+	assert.True(valid)
+	assert.Empty(vals)
+
+	vals, valid = RouteChecker{
+		Namespaces:     []string{"test"},
+		VirtualService: fakeOneTcpRouteNoWeight(),
+	}.Check()
+
+	assert.True(valid)
+	assert.Empty(vals)
+
+	vals, valid = RouteChecker{
+		Namespaces:     []string{"test"},
+		VirtualService: fakeOneTlsRouteNoWeight(),
+	}.Check()
+
+	assert.True(valid)
+	assert.Empty(vals)
 }
 
 func TestVSWithRepeatingSubsets(t *testing.T) {
@@ -98,6 +153,46 @@ func fakeOneRouteUnder100() *networking_v1.VirtualService {
 	return virtualService
 }
 
+func fakeOneTcpRouteUnder100() *networking_v1.VirtualService {
+	virtualService := data.AddTcpRoutesToVirtualService(data.CreateTcpRoute("reviews", "v1", 45),
+		data.CreateEmptyVirtualService("reviews-multiple", "test", []string{"reviews"}),
+	)
+
+	return virtualService
+}
+
+func fakeOneTlsRouteUnder100() *networking_v1.VirtualService {
+	virtualService := data.AddTlsRoutesToVirtualService(data.CreateTlsRoute("reviews", "v1", 45),
+		data.CreateEmptyVirtualService("reviews-multiple", "test", []string{"reviews"}),
+	)
+
+	return virtualService
+}
+
+func fakeOneRouteNoWeight() *networking_v1.VirtualService {
+	virtualService := data.AddHttpRoutesToVirtualService(data.CreateHttpRouteDestination("reviews", "v1", 0),
+		data.CreateEmptyVirtualService("reviews-multiple", "test", []string{"reviews"}),
+	)
+
+	return virtualService
+}
+
+func fakeOneTcpRouteNoWeight() *networking_v1.VirtualService {
+	virtualService := data.AddTcpRoutesToVirtualService(data.CreateTcpRoute("reviews", "v1", 0),
+		data.CreateEmptyVirtualService("reviews-multiple", "test", []string{"reviews"}),
+	)
+
+	return virtualService
+}
+
+func fakeOneTlsRouteNoWeight() *networking_v1.VirtualService {
+	virtualService := data.AddTlsRoutesToVirtualService(data.CreateTlsRoute("reviews", "v1", 0),
+		data.CreateEmptyVirtualService("reviews-multiple", "test", []string{"reviews"}),
+	)
+
+	return virtualService
+}
+
 func fakeRepeatedSubset() *networking_v1.VirtualService {
 	validVirtualService := data.AddHttpRoutesToVirtualService(data.CreateHttpRouteDestination("reviews", "v1", 55),
 		data.AddHttpRoutesToVirtualService(data.CreateHttpRouteDestination("reviews", "v1", 45),
diff --git a/business/test_util.go b/business/test_util.go
index cba06a570..51da97f0a 100644
--- a/business/test_util.go
+++ b/business/test_util.go
@@ -941,10 +941,7 @@ func FakeWaypointPod() []core_v1.Pod {
 			},
 			Spec: core_v1.PodSpec{
 				Containers: []core_v1.Container{
-					{Name: "waypoint-dcd74f8b4-nf7jc", Image: "whatever"},
-				},
-				InitContainers: []core_v1.Container{
-					{Name: "istio-init", Image: "gcr.io/istio-release/proxyv2:1.22.0-distroless"},
+					{Name: "waypoint-dcd74f8b4-nf7jc", Image: "gcr.io/istio-release/proxyv2:1.24.1-distroless"},
 				},
 			},
 		},
@@ -976,9 +973,6 @@ func FakeWaypointNamespaceEnrolledPods(waypoint bool) []core_v1.Pod {
 				Containers: []core_v1.Container{
 					{Name: "details", Image: "whatever"},
 				},
-				InitContainers: []core_v1.Container{
-					{Name: "istio-init", Image: "docker.io/istio/proxy_init:0.7.1"},
-				},
 			},
 		},
 		{ObjectMeta: meta_v1.ObjectMeta{
@@ -992,9 +986,6 @@ func FakeWaypointNamespaceEnrolledPods(waypoint bool) []core_v1.Pod {
 				Containers: []core_v1.Container{
 					{Name: "productpage", Image: "whatever"},
 				},
-				InitContainers: []core_v1.Container{
-					{Name: "istio-init", Image: "docker.io/istio/proxy_init:0.7.1"},
-				},
 			},
 		},
 	}
diff --git a/business/workloads_test.go b/business/workloads_test.go
index 48135260e..c9f2de9f8 100644
--- a/business/workloads_test.go
+++ b/business/workloads_test.go
@@ -1133,7 +1133,7 @@ func TestValidateWaypoint(t *testing.T) {
 	require.NotNil(workload)
 
 	assert.Equal(1, len(workload.Pods))
-	assert.True(workload.Pods[0].IsWaypoint())
+	assert.True(workload.IsWaypoint())
 
 	// Get waypoint proxy
 	criteriaDetails := WorkloadCriteria{Cluster: conf.KubernetesConfig.ClusterName, Namespace: "Namespace", WorkloadName: "details", WorkloadGVK: schema.GroupVersionKind{}, IncludeServices: false}
@@ -1205,7 +1205,7 @@ func TestValidateWaypointNS(t *testing.T) {
 	require.NotNil(workload)
 
 	assert.Equal(1, len(workload.Pods))
-	assert.True(workload.Pods[0].IsWaypoint())
+	assert.True(workload.IsWaypoint())
 
 	// Get waypoint proxy
 	criteriaDetails := WorkloadCriteria{Cluster: conf.KubernetesConfig.ClusterName, Namespace: "Namespace", WorkloadName: "details", WorkloadGVK: schema.GroupVersionKind{}, IncludeServices: false}
@@ -1270,7 +1270,7 @@ func TestValidateWaypointService(t *testing.T) {
 	require.NotNil(workload)
 
 	assert.Equal(1, len(workload.Pods))
-	assert.True(workload.Pods[0].IsWaypoint())
+	assert.True(workload.IsWaypoint())
 
 	// Get waypoint proxy
 	criteriaDetails := WorkloadCriteria{Cluster: conf.KubernetesConfig.ClusterName, Namespace: "Namespace", WorkloadName: "details", WorkloadGVK: schema.GroupVersionKind{}, IncludeServices: false}
@@ -1294,6 +1294,72 @@ func TestValidateWaypointService(t *testing.T) {
 	assert.Equal(0, len(workloadProductPage.WaypointWorkloads))
 }
 
+// TestValidateWaypoint validate waypoint proxy status
+func TestValidateWaypointProxyStatus(t *testing.T) {
+	assert := assert.New(t)
+	require := require.New(t)
+	conf := config.NewConfig()
+	config.Set(conf)
+
+	// Setup mocks
+	kubeObjs := []runtime.Object{
+		&osproject_v1.Project{ObjectMeta: v1.ObjectMeta{Name: "Namespace"}},
+	}
+
+	for _, obj := range FakeWaypointPod() {
+		o := obj
+		kubeObjs = append(kubeObjs, &o)
+	}
+
+	for _, obj := range FakeWaypointNamespaceEnrolledPods(true) {
+		o := obj
+		kubeObjs = append(kubeObjs, &o)
+	}
+
+	k8s := kubetest.NewFakeK8sClient(kubeObjs...)
+	k8s.OpenShift = true
+	SetupBusinessLayer(t, k8s, *conf)
+	svc := setupWorkloadService(k8s, conf)
+
+	// Set cache proxy status
+	ps := kubernetes.ProxyStatus{
+		Pilot: "istiod-54b6586f84-9bgw2",
+		SyncStatus: kubernetes.SyncStatus{ClusterID: "",
+			ProxyID:       "waypoint.Namespace",
+			ProxyVersion:  "",
+			IstioVersion:  "1.24.1",
+			ClusterSent:   "2024-12-05T09:04:18Z/388d498c74-bc9a-4fa8-a658-36fe2a752d32",
+			ClusterAcked:  "2024-12-05T09:04:18Z/388d498c74-bc9a-4fa8-a658-36fe2a752d32",
+			ListenerSent:  "2024-12-05T09:04:18Z/38936e5b48-b856-4ec4-94b0-0ef0f085aca5",
+			ListenerAcked: "2024-12-05T09:04:18Z/38936e5b48-b856-4ec4-94b0-0ef0f085aca5",
+			RouteSent:     "",
+			RouteAcked:    "",
+			EndpointSent:  "2024-12-05T09:04:18Z/3889678ca1-9e60-4c11-973e-c7fdbc338d92",
+			EndpointAcked: "2024-12-05T09:04:18Z/3889678ca1-9e60-4c11-973e-c7fdbc338d92",
+		},
+	}
+	psArr := []*kubernetes.ProxyStatus{&ps}
+	svc.cache.SetPodProxyStatus(psArr)
+
+	// Get waypoint proxy
+	criteria := WorkloadCriteria{Cluster: conf.KubernetesConfig.ClusterName, Namespace: "Namespace", WorkloadName: "waypoint", WorkloadGVK: schema.GroupVersionKind{}, IncludeServices: false}
+	workload, err := svc.GetWorkload(context.TODO(), criteria)
+
+	require.NoError(err)
+	require.NotNil(workload)
+
+	assert.Equal(1, len(workload.Pods))
+	assert.False(workload.IsGateway())
+	assert.True(workload.IsWaypoint())
+
+	pod := workload.Pods[0]
+	assert.NotNil(pod.ProxyStatus)
+	assert.Equal(pod.ProxyStatus.CDS, "Synced")
+	assert.Equal(pod.ProxyStatus.EDS, "Synced")
+	assert.Equal(pod.ProxyStatus.LDS, "Synced")
+	assert.Equal(pod.ProxyStatus.RDS, "IGNORED")
+}
+
 func TestGetWorkloadSetsIsGateway(t *testing.T) {
 	require := require.New(t)
 	conf := config.NewConfig()
diff --git a/graph/api/api_test.go b/graph/api/api_test.go
index 9b4923434..812f99b91 100644
--- a/graph/api/api_test.go
+++ b/graph/api/api_test.go
@@ -3907,7 +3907,7 @@ func ambientWorkloads(t *testing.T) *business.Layer {
 			},
 		}}
 
-	ns := kubetest.FakeNamespace("bookinfo")
+	ns := kubetest.FakeNamespaceWithLabels("bookinfo", map[string]string{"istio.io/dataplane-mode": "ambient"})
 	k8s := kubetest.NewFakeK8sClient(k8spod1, k8spod2, k8spod3, k8spod4, ns)
 	conf := config.NewConfig()
 	conf.ExternalServices.Istio.IstioAPIEnabled = false
@@ -3922,11 +3922,15 @@ func ambientWorkloads(t *testing.T) *business.Layer {
 }
 
 func ambientMockGraph(api *prometheustest.PromAPIMock) {
+
 	q0 := `round(sum(rate(istio_requests_total{reporter=~"source|waypoint",source_workload_namespace!="bookinfo",destination_workload_namespace="unknown",destination_workload="unknown",destination_service=~"^.+\\.bookinfo\\..+$"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)`
 	v0 := model.Vector{}
 
-	q1 := `round(sum(rate(istio_requests_total{reporter=~"destination|waypoint",destination_workload_namespace="bookinfo"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)`
-	q1m0 := model.Metric{
+	q1 := `round(sum(rate(istio_requests_total{reporter="source",source_workload_namespace!="bookinfo",destination_workload_namespace="bookinfo"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)`
+	v1 := model.Vector{}
+
+	q2 := `round(sum(rate(istio_requests_total{reporter=~"destination|waypoint",destination_workload_namespace="bookinfo"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)`
+	q2m0 := model.Metric{
 		"source_workload_namespace":      "bookinfo",
 		"source_workload":                "productpage-v1",
 		"source_canonical_service":       "productpage",
@@ -3945,7 +3949,7 @@ func ambientMockGraph(api *prometheustest.PromAPIMock) {
 		"grpc_response_status":           "0",
 		"response_flags":                 "-",
 	}
-	q1m1 := model.Metric{
+	q2m1 := model.Metric{
 		"source_workload_namespace":      "bookinfo",
 		"source_workload":                "productpage-v1",
 		"source_canonical_service":       "productpage",
@@ -3964,19 +3968,19 @@ func ambientMockGraph(api *prometheustest.PromAPIMock) {
 		"grpc_response_status":           "0",
 		"response_flags":                 "-",
 	}
-	v1 := model.Vector{
+	v2 := model.Vector{
 		&model.Sample{
-			Metric: q1m0,
+			Metric: q2m0,
 			Value:  10,
 		},
 		&model.Sample{
-			Metric: q1m1,
+			Metric: q2m1,
 			Value:  10,
 		},
 	}
 
-	q2 := `round(sum(rate(istio_requests_total{reporter=~"source|waypoint",source_workload_namespace="bookinfo"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)`
-	q2m0 := model.Metric{
+	q3 := `round(sum(rate(istio_requests_total{reporter=~"source|waypoint",source_workload_namespace="bookinfo"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)`
+	q3m0 := model.Metric{
 		"source_workload_namespace":      "bookinfo",
 		"source_workload":                "productpage-v1",
 		"source_canonical_service":       "productpage",
@@ -3995,7 +3999,7 @@ func ambientMockGraph(api *prometheustest.PromAPIMock) {
 		"grpc_response_status":           "0",
 		"response_flags":                 "-",
 	}
-	q2m1 := model.Metric{
+	q3m1 := model.Metric{
 		"source_workload_namespace":      "bookinfo",
 		"source_workload":                "productpage-v1",
 		"source_canonical_service":       "productpage",
@@ -4014,22 +4018,22 @@ func ambientMockGraph(api *prometheustest.PromAPIMock) {
 		"grpc_response_status":           "0",
 		"response_flags":                 "-",
 	}
-	v2 := model.Vector{
+	v3 := model.Vector{
 		&model.Sample{
-			Metric: q2m0,
+			Metric: q3m0,
 			Value:  10,
 		},
 		&model.Sample{
-			Metric: q2m1,
+			Metric: q3m1,
 			Value:  10,
 		},
 	}
 
-	q3 := `round(sum(rate(istio_tcp_received_bytes_total{reporter=~"source|waypoint",source_workload_namespace!="bookinfo",destination_workload_namespace="unknown",destination_workload="unknown",destination_service=~"^.+\\.bookinfo\\..+$"} [600s])) by (app,source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)`
-	v3 := model.Vector{}
+	q4 := `round(sum(rate(istio_tcp_received_bytes_total{reporter=~"source|waypoint",source_workload_namespace!="bookinfo",destination_workload_namespace="unknown",destination_workload="unknown",destination_service=~"^.+\\.bookinfo\\..+$"} [600s])) by (app,source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)`
+	v4 := model.Vector{}
 
-	q4 := `round(sum(rate(istio_tcp_received_bytes_total{reporter=~"destination|waypoint",destination_workload_namespace="bookinfo"} [600s])) by (app,source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)`
-	q4m0 := model.Metric{
+	q5 := `round(sum(rate(istio_tcp_received_bytes_total{reporter=~"destination|waypoint",destination_workload_namespace="bookinfo"} [600s])) by (app,source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)`
+	q5m0 := model.Metric{
 		"source_workload_namespace":      "bookinfo",
 		"source_workload":                "waypoint",
 		"source_canonical_service":       "waypoint",
@@ -4046,7 +4050,7 @@ func ambientMockGraph(api *prometheustest.PromAPIMock) {
 		"response_flags":                 "-",
 		"app":                            "ztunnel",
 	}
-	q4m1 := model.Metric{
+	q5m1 := model.Metric{
 		"source_workload_namespace":      "bookinfo",
 		"source_workload":                "waypoint",
 		"source_canonical_service":       "waypoint",
@@ -4063,7 +4067,7 @@ func ambientMockGraph(api *prometheustest.PromAPIMock) {
 		"response_flags":                 "-",
 		"app":                            "ztunnel",
 	}
-	q4m2 := model.Metric{
+	q5m2 := model.Metric{
 		"source_workload_namespace":      "bookinfo",
 		"source_workload":                "waypoint",
 		"source_canonical_service":       "waypoint",
@@ -4080,23 +4084,23 @@ func ambientMockGraph(api *prometheustest.PromAPIMock) {
 		"response_flags":                 "-",
 		"app":                            "ztunnel",
 	}
-	v4 := model.Vector{
+	v5 := model.Vector{
 		&model.Sample{
-			Metric: q4m0,
+			Metric: q5m0,
 			Value:  150,
 		},
 		&model.Sample{
-			Metric: q4m1,
+			Metric: q5m1,
 			Value:  50,
 		},
 		&model.Sample{
-			Metric: q4m2,
+			Metric: q5m2,
 			Value:  50,
 		},
 	}
 
-	q5 := `round(sum(rate(istio_tcp_received_bytes_total{reporter=~"source|waypoint",source_workload_namespace="bookinfo"} [600s])) by (app,source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)`
-	q5m0 := model.Metric{
+	q6 := `round(sum(rate(istio_tcp_received_bytes_total{reporter=~"source|waypoint",source_workload_namespace="bookinfo"} [600s])) by (app,source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)`
+	q6m0 := model.Metric{
 		"source_workload_namespace":      "bookinfo",
 		"source_workload":                "productpage-v1",
 		"source_canonical_service":       "productpage",
@@ -4113,7 +4117,7 @@ func ambientMockGraph(api *prometheustest.PromAPIMock) {
 		"response_flags":                 "-",
 		"app":                            "ztunnel",
 	}
-	q5m1 := model.Metric{
+	q6m1 := model.Metric{
 		"source_workload_namespace":      "bookinfo",
 		"source_workload":                "productpage-v1",
 		"source_canonical_service":       "productpage",
@@ -4130,13 +4134,13 @@ func ambientMockGraph(api *prometheustest.PromAPIMock) {
 		"response_flags":                 "-",
 		"app":                            "ztunnel",
 	}
-	v5 := model.Vector{
+	v6 := model.Vector{
 		&model.Sample{
-			Metric: q5m0,
+			Metric: q6m0,
 			Value:  50,
 		},
 		&model.Sample{
-			Metric: q5m1,
+			Metric: q6m1,
 			Value:  50,
 		},
 	}
@@ -4147,6 +4151,7 @@ func ambientMockGraph(api *prometheustest.PromAPIMock) {
 	mockQuery(api, q3, &v3)
 	mockQuery(api, q4, &v4)
 	mockQuery(api, q5, &v5)
+	mockQuery(api, q6, &v6)
 }
 
 // TestAmbientGraph tests some waypoint-specific graph features
diff --git a/graph/telemetry/istio/appender/response_time_test.go b/graph/telemetry/istio/appender/response_time_test.go
index ab1ec904e..c01a00391 100644
--- a/graph/telemetry/istio/appender/response_time_test.go
+++ b/graph/telemetry/istio/appender/response_time_test.go
@@ -14,8 +14,11 @@ import (
 func TestResponseTimeP95(t *testing.T) {
 	assert := assert.New(t)
 
-	q0 := `round(histogram_quantile(0.95, sum(rate(istio_request_duration_milliseconds_bucket{reporter=~"destination|waypoint",destination_service_namespace="bookinfo"}[60s])) by (le,source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol)) > 0,0.001)`
-	q0m0 := model.Metric{
+	q0 := `round(histogram_quantile(0.95, sum(rate(istio_request_duration_milliseconds_bucket{reporter="source",source_workload_namespace!="bookinfo",destination_service_namespace="bookinfo"}[60s])) by (le,source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol)) > 0,0.001)`
+	v0 := model.Vector{}
+
+	q1 := `round(histogram_quantile(0.95, sum(rate(istio_request_duration_milliseconds_bucket{reporter=~"destination|waypoint",destination_service_namespace="bookinfo"}[60s])) by (le,source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol)) > 0,0.001)`
+	q1m0 := model.Metric{
 		"source_cluster":                 config.DefaultClusterID,
 		"source_workload_namespace":      "istio-system",
 		"source_workload":                "ingressgateway-unknown",
@@ -30,7 +33,7 @@ func TestResponseTimeP95(t *testing.T) {
 		"destination_canonical_service":  "productpage",
 		"destination_canonical_revision": "v1",
 		"request_protocol":               "http"}
-	q0m1 := model.Metric{
+	q1m1 := model.Metric{
 		"source_cluster":                 config.DefaultClusterID,
 		"source_workload_namespace":      "bookinfo",
 		"source_workload":                "productpage-v1",
@@ -45,7 +48,7 @@ func TestResponseTimeP95(t *testing.T) {
 		"destination_canonical_service":  "reviews",
 		"destination_canonical_revision": "v1",
 		"request_protocol":               "http"}
-	q0m2 := model.Metric{
+	q1m2 := model.Metric{
 		"source_cluster":                 config.DefaultClusterID,
 		"source_workload_namespace":      "bookinfo",
 		"source_workload":                "productpage-v1",
@@ -60,7 +63,7 @@ func TestResponseTimeP95(t *testing.T) {
 		"destination_canonical_service":  "reviews",
 		"destination_canonical_revision": "v2",
 		"request_protocol":               "http"}
-	q0m3 := model.Metric{
+	q1m3 := model.Metric{
 		"source_cluster":                 config.DefaultClusterID,
 		"source_workload_namespace":      "bookinfo",
 		"source_workload":                "reviews-v1",
@@ -75,7 +78,7 @@ func TestResponseTimeP95(t *testing.T) {
 		"destination_canonical_service":  "ratings",
 		"destination_canonical_revision": "v1",
 		"request_protocol":               "http"}
-	q0m4 := model.Metric{
+	q1m4 := model.Metric{
 		"source_cluster":                 config.DefaultClusterID,
 		"source_workload_namespace":      "bookinfo",
 		"source_workload":                "reviews-v2",
@@ -90,26 +93,26 @@ func TestResponseTimeP95(t *testing.T) {
 		"destination_canonical_service":  "ratings",
 		"destination_canonical_revision": "v1",
 		"request_protocol":               "http"}
-	v0 := model.Vector{
+	v1 := model.Vector{
 		&model.Sample{
-			Metric: q0m0,
+			Metric: q1m0,
 			Value:  0.010},
 		&model.Sample{
-			Metric: q0m1,
+			Metric: q1m1,
 			Value:  0.020},
 		&model.Sample{
-			Metric: q0m2,
+			Metric: q1m2,
 			Value:  0.020},
 		&model.Sample{
-			Metric: q0m3,
+			Metric: q1m3,
 			Value:  0.030}, // same edge reported by outgoing (q1), this > value should be preferred
 		&model.Sample{
-			Metric: q0m4,
+			Metric: q1m4,
 			Value:  0.030}, // same edge reported by outgoing (q1), this > value should be preferred
 	}
 
-	q1 := `round(histogram_quantile(0.95, sum(rate(istio_request_duration_milliseconds_bucket{reporter=~"source|waypoint",source_workload_namespace="bookinfo"}[60s])) by (le,source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol)) > 0,0.001)`
-	q1m0 := model.Metric{
+	q2 := `round(histogram_quantile(0.95, sum(rate(istio_request_duration_milliseconds_bucket{reporter=~"source|waypoint",source_workload_namespace="bookinfo"}[60s])) by (le,source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol)) > 0,0.001)`
+	q2m0 := model.Metric{
 		"source_cluster":                 config.DefaultClusterID,
 		"source_workload_namespace":      "bookinfo",
 		"source_workload":                "productpage-v1",
@@ -124,7 +127,7 @@ func TestResponseTimeP95(t *testing.T) {
 		"destination_canonical_service":  "reviews",
 		"destination_canonical_revision": "v1",
 		"request_protocol":               "http"}
-	q1m1 := model.Metric{
+	q2m1 := model.Metric{
 		"source_cluster":                 config.DefaultClusterID,
 		"source_workload_namespace":      "bookinfo",
 		"source_workload":                "productpage-v1",
@@ -139,7 +142,7 @@ func TestResponseTimeP95(t *testing.T) {
 		"destination_canonical_service":  "reviews",
 		"destination_canonical_revision": "v2",
 		"request_protocol":               "http"}
-	q1m2 := model.Metric{
+	q2m2 := model.Metric{
 		"source_cluster":                 config.DefaultClusterID,
 		"source_workload_namespace":      "bookinfo",
 		"source_workload":                "reviews-v1",
@@ -154,7 +157,7 @@ func TestResponseTimeP95(t *testing.T) {
 		"destination_canonical_service":  "ratings",
 		"destination_canonical_revision": "v1",
 		"request_protocol":               "http"}
-	q1m3 := model.Metric{
+	q2m3 := model.Metric{
 		"source_cluster":                 config.DefaultClusterID,
 		"source_workload_namespace":      "bookinfo",
 		"source_workload":                "reviews-v2",
@@ -170,18 +173,18 @@ func TestResponseTimeP95(t *testing.T) {
 		"destination_canonical_revision": "v1",
 		"request_protocol":               "http"}
 
-	v1 := model.Vector{
+	v2 := model.Vector{
 		&model.Sample{
-			Metric: q1m0,
+			Metric: q2m0,
 			Value:  0.020},
 		&model.Sample{
-			Metric: q1m1,
+			Metric: q2m1,
 			Value:  0.020},
 		&model.Sample{
-			Metric: q1m2,
+			Metric: q2m2,
 			Value:  0.040}, // same edge reported by incoming (q0), this > value should get ignored
 		&model.Sample{
-			Metric: q1m3,
+			Metric: q2m3,
 			Value:  0.040}, // same edge reported by incoming (q0), this > value should get ignored
 	}
 
@@ -192,6 +195,7 @@ func TestResponseTimeP95(t *testing.T) {
 	}
 	mockQuery(api, q0, &v0)
 	mockQuery(api, q1, &v1)
+	mockQuery(api, q2, &v2)
 
 	trafficMap := responseTimeTestTraffic()
 	ingressID, _, _ := graph.Id(config.DefaultClusterID, "istio-system", "", "istio-system", "ingressgateway-unknown", "ingressgateway", graph.Unknown, graph.GraphTypeVersionedApp)
@@ -207,8 +211,9 @@ func TestResponseTimeP95(t *testing.T) {
 		InjectServiceNodes: true,
 		Namespaces: map[string]graph.NamespaceInfo{
 			"bookinfo": {
-				Name:     "bookinfo",
-				Duration: duration,
+				Name:      "bookinfo",
+				Duration:  duration,
+				IsAmbient: true,
 			},
 		},
 		Quantile:  0.95,
@@ -221,7 +226,7 @@ func TestResponseTimeP95(t *testing.T) {
 		},
 	}
 
-	appender.appendGraph(trafficMap, "bookinfo", client)
+	appender.appendGraph(trafficMap, graph.NamespaceInfo{Name: "bookinfo", IsAmbient: true}, client)
 
 	ingress, ok = trafficMap[ingressID]
 	assert.Equal(true, ok)
@@ -495,7 +500,7 @@ func TestResponseTimeAvgSkipRates(t *testing.T) {
 		},
 	}
 
-	appender.appendGraph(trafficMap, "bookinfo", client)
+	appender.appendGraph(trafficMap, graph.NamespaceInfo{Name: "bookinfo", IsAmbient: false}, client)
 
 	ingress, ok = trafficMap[ingressID]
 	assert.Equal(true, ok)
@@ -769,7 +774,7 @@ func TestResponseTimeAvg(t *testing.T) {
 		},
 	}
 
-	appender.appendGraph(trafficMap, "bookinfo", client)
+	appender.appendGraph(trafficMap, graph.NamespaceInfo{Name: "bookinfo", IsAmbient: false}, client)
 
 	ingress, ok = trafficMap[ingressID]
 	assert.Equal(true, ok)
diff --git a/kubernetes/cache/cache_test.go b/kubernetes/cache/cache_test.go
index 930eb997e..76c9e5094 100644
--- a/kubernetes/cache/cache_test.go
+++ b/kubernetes/cache/cache_test.go
@@ -1,6 +1,8 @@
 package cache_test
 
 import (
+	"encoding/json"
+	"os"
 	"sync"
 	"testing"
 
@@ -257,3 +259,37 @@ func TestValidationsSetByConstructor(t *testing.T) {
 
 	require.NotNil(cache.Validations())
 }
+
+func TestZtunnelDump(t *testing.T) {
+	require := require.New(t)
+	conf := config.NewConfig()
+
+	clients := map[string]kubernetes.ClientInterface{conf.KubernetesConfig.ClusterName: kubetest.NewFakeK8sClient()}
+	cache, err := cache.NewKialiCache(clients, *conf)
+	require.NoError(err)
+
+	initData := cache.GetZtunnelDump("cluster-default", "istio-system", "ztunnel-7hml8")
+	require.Nil(initData)
+
+	zTunnel := make(map[string]*kubernetes.ZtunnelConfigDump)
+	zTunnelData, err := os.Open("../testdata/ztunnel-config.json")
+	require.NoError(err)
+	defer zTunnelData.Close()
+
+	configD := &kubernetes.ZtunnelConfigDump{}
+	errD := json.NewDecoder(zTunnelData).Decode(&configD)
+	require.NoError(errD)
+
+	zTunnel["cluster-defaultistio-systemztunnel-7hml8"] = configD
+	cache.SetZtunnelDump(zTunnel)
+
+	cacheData := cache.GetZtunnelDump("cluster-default", "istio-system", "ztunnel-7hml8")
+	require.NotNil(cacheData)
+	require.Equal(cacheData.Workloads[1].Name, "details-v1-7c5d957895-pwqdh")
+	require.Equal(cacheData.Workloads[1].Protocol, "HBONE")
+	require.Equal(cacheData.Workloads[1].Node, "ci-worker")
+
+	require.Equal(cacheData.Services[36].Hostname, "waypoint.bookinfo.svc.cluster.local")
+	require.Equal(cacheData.Services[36].Name, "waypoint")
+	require.Equal(cacheData.Services[36].Namespace, "bookinfo")
+}
diff --git a/kubernetes/kubetest/mock_istio.go b/kubernetes/kubetest/mock_istio.go
index eeba500ce..f2f50dd0d 100644
--- a/kubernetes/kubetest/mock_istio.go
+++ b/kubernetes/kubetest/mock_istio.go
@@ -57,6 +57,11 @@ func (o *K8SClientMock) GetConfigDump(namespace string, podName string) (*kubern
 	return args.Get(0).(*kubernetes.ConfigDump), args.Error(1)
 }
 
+func (o *K8SClientMock) GetZtunnelConfigDump(namespace string, podName string) (*kubernetes.ZtunnelConfigDump, error) {
+	args := o.Called(namespace, podName)
+	return args.Get(0).(*kubernetes.ZtunnelConfigDump), args.Error(1)
+}
+
 func (o *K8SClientMock) GetRegistryServices() ([]*kubernetes.RegistryService, error) {
 	args := o.Called()
 	return args.Get(0).([]*kubernetes.RegistryService), args.Error(1)
diff --git a/kubernetes/testdata/ztunnel-config.json b/kubernetes/testdata/ztunnel-config.json
new file mode 100644
index 000000000..1a1742af6
--- /dev/null
+++ b/kubernetes/testdata/ztunnel-config.json
@@ -0,0 +1,1928 @@
+{
+  "certificates": [],
+  "config": {
+    "adminAddr": {
+      "Localhost": [true, 15000]
+    },
+    "altCaHostname": null,
+    "altXdsHostname": null,
+    "caAddress": "https://istiod.istio-system.svc:15012",
+    "caRootCert": {
+      "File": "./var/run/secrets/istio/root-cert.pem"
+    },
+    "clusterDomain": "cluster.local",
+    "clusterId": "cluster-default",
+    "connectionWindowSize": 4194304,
+    "dnsProxy": true,
+    "dnsProxyAddr": {
+      "Localhost": [true, 15053]
+    },
+    "dnsResolverCfg": {
+      "domain": null,
+      "name_servers": [
+        {
+          "bind_addr": null,
+          "protocol": "udp",
+          "socket_addr": "10.96.0.10:53",
+          "tls_dns_name": null,
+          "trust_negative_responses": false
+        },
+        {
+          "bind_addr": null,
+          "protocol": "tcp",
+          "socket_addr": "10.96.0.10:53",
+          "tls_dns_name": null,
+          "trust_negative_responses": false
+        }
+      ],
+      "search": [
+        "istio-system.svc.cluster.local",
+        "svc.cluster.local",
+        "cluster.local",
+        "home"
+      ]
+    },
+    "dnsResolverOpts": {
+      "attempts": 2,
+      "authentic_data": false,
+      "cache_size": 4096,
+      "check_names": true,
+      "edns0": false,
+      "ip_strategy": "Ipv4AndIpv6",
+      "ndots": 5,
+      "negative_max_ttl": null,
+      "negative_min_ttl": null,
+      "num_concurrent_reqs": 2,
+      "positive_max_ttl": null,
+      "positive_min_ttl": null,
+      "preserve_intermediates": true,
+      "recursion_desired": true,
+      "rotate": false,
+      "server_ordering_strategy": "QueryStatistics",
+      "shuffle_dns_servers": false,
+      "timeout": {
+        "nanos": 0,
+        "secs": 5
+      },
+      "try_tcp_on_error": false,
+      "use_hosts_file": true,
+      "validate": false
+    },
+    "fakeCa": false,
+    "fakeSelfInbound": false,
+    "frameSize": 1048576,
+    "illegalPorts": [15001, 15006, 15008],
+    "inboundAddr": "[::]:15008",
+    "inboundPlaintextAddr": "[::]:15006",
+    "inpodPortReuse": true,
+    "inpodUds": "/var/run/ztunnel/ztunnel.sock",
+    "localNode": "ci-control-plane",
+    "network": "",
+    "numWorkerThreads": 2,
+    "outboundAddr": "[::]:15001",
+    "packetMark": 1337,
+    "poolMaxStreamsPerConn": 100,
+    "poolUnusedReleaseTimeout": {
+      "nanos": 0,
+      "secs": 300
+    },
+    "proxy": true,
+    "proxyArgs": "proxy ztunnel",
+    "proxyMetadata": {
+      "CLUSTER_ID": "cluster-default",
+      "ENABLE_HBONE": "true",
+      "ISTIO_VERSION": "1.24.1"
+    },
+    "proxyMode": "Shared",
+    "proxyWorkloadInformation": null,
+    "readinessAddr": {
+      "SocketAddr": "[::]:15021"
+    },
+    "requireOriginalSource": null,
+    "secretTtl": {
+      "nanos": 0,
+      "secs": 86400
+    },
+    "selfTerminationDeadline": {
+      "nanos": 0,
+      "secs": 25
+    },
+    "socks5Addr": null,
+    "statsAddr": {
+      "SocketAddr": "[::]:15020"
+    },
+    "windowSize": 4194304,
+    "xdsAddress": "https://istiod.istio-system.svc:15012",
+    "xdsOnDemand": false,
+    "xdsRootCert": {
+      "File": "./var/run/secrets/istio/root-cert.pem"
+    }
+  },
+  "policies": [],
+  "services": [
+    {
+      "endpoints": {
+        "cluster-default//Pod/bookinfo/bookinfo-gateway-istio-7c875f844-dllvg": {
+          "port": {
+            "15021": 15021,
+            "80": 80
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/bookinfo/bookinfo-gateway-istio-7c875f844-dllvg"
+        }
+      },
+      "hostname": "bookinfo-gateway-istio.bookinfo.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "bookinfo-gateway-istio",
+      "namespace": "bookinfo",
+      "ports": {
+        "15021": 15021,
+        "80": 80
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.57.63"],
+      "waypoint": {
+        "destination": "bookinfo/waypoint.bookinfo.svc.cluster.local",
+        "hboneMtlsPort": 15008
+      }
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/travel-agency/cars-v1-8f6f65b75-f62nc": {
+          "port": {
+            "8000": 8000
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/travel-agency/cars-v1-8f6f65b75-f62nc"
+        }
+      },
+      "hostname": "cars.travel-agency.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "cars",
+      "namespace": "travel-agency",
+      "ports": {
+        "8000": 8000
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.137.86"]
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/travel-control/control-57d5847d68-74kjs": {
+          "port": {
+            "8080": 8080
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/travel-control/control-57d5847d68-74kjs"
+        }
+      },
+      "hostname": "control.travel-control.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "control",
+      "namespace": "travel-control",
+      "ports": {
+        "8080": 8080
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.49.97"]
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/bookinfo/details-v1-7c5d957895-pwqdh": {
+          "port": {
+            "9080": 9080
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/bookinfo/details-v1-7c5d957895-pwqdh"
+        }
+      },
+      "hostname": "details-v1.bookinfo.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "details-v1",
+      "namespace": "bookinfo",
+      "ports": {
+        "9080": 9080
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.221.215"],
+      "waypoint": {
+        "destination": "bookinfo/waypoint.bookinfo.svc.cluster.local",
+        "hboneMtlsPort": 15008
+      }
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/bookinfo/details-v1-7c5d957895-pwqdh": {
+          "port": {
+            "9080": 9080
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/bookinfo/details-v1-7c5d957895-pwqdh"
+        }
+      },
+      "hostname": "details.bookinfo.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "details",
+      "namespace": "bookinfo",
+      "ports": {
+        "9080": 9080
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.49.172"],
+      "waypoint": {
+        "destination": "bookinfo/waypoint.bookinfo.svc.cluster.local",
+        "hboneMtlsPort": 15008
+      }
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/travel-agency/discounts-v1-58c9676688-sx2vp": {
+          "port": {
+            "8000": 8000
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/travel-agency/discounts-v1-58c9676688-sx2vp"
+        }
+      },
+      "hostname": "discounts.travel-agency.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "discounts",
+      "namespace": "travel-agency",
+      "ports": {
+        "8000": 8000
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.151.162"]
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/travel-agency/flights-v1-6775bb778f-d88gr": {
+          "port": {
+            "8000": 8000
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/travel-agency/flights-v1-6775bb778f-d88gr"
+        }
+      },
+      "hostname": "flights.travel-agency.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "flights",
+      "namespace": "travel-agency",
+      "ports": {
+        "8000": 8000
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.34.73"]
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/istio-system/grafana-7874f88995-49vm4": {
+          "port": {
+            "3000": 3000
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/istio-system/grafana-7874f88995-49vm4"
+        }
+      },
+      "hostname": "grafana.istio-system.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "grafana",
+      "namespace": "istio-system",
+      "ports": {
+        "3000": 3000
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.73.168"]
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/travel-agency/hotels-v1-5799fbd56c-bwpsb": {
+          "port": {
+            "8000": 8000
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/travel-agency/hotels-v1-5799fbd56c-bwpsb"
+        }
+      },
+      "hostname": "hotels.travel-agency.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "hotels",
+      "namespace": "travel-agency",
+      "ports": {
+        "8000": 8000
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.201.105"]
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/travel-agency/insurances-v1-96d96c758-qs6sn": {
+          "port": {
+            "8000": 8000
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/travel-agency/insurances-v1-96d96c758-qs6sn"
+        }
+      },
+      "hostname": "insurances.travel-agency.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "insurances",
+      "namespace": "travel-agency",
+      "ports": {
+        "8000": 8000
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.70.24"]
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/istio-system/istio-egressgateway-66db7db9d4-kjcmw": {
+          "port": {
+            "443": 8443,
+            "80": 8080
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/istio-system/istio-egressgateway-66db7db9d4-kjcmw"
+        }
+      },
+      "hostname": "istio-egressgateway.istio-system.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "istio-egressgateway",
+      "namespace": "istio-system",
+      "ports": {
+        "443": 8443,
+        "80": 8080
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.20.75"]
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/istio-system/istio-ingressgateway-74d79d79fc-q6x6q": {
+          "port": {
+            "15021": 15021,
+            "443": 8443,
+            "80": 8080
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/istio-system/istio-ingressgateway-74d79d79fc-q6x6q"
+        }
+      },
+      "hostname": "istio-ingressgateway.istio-system.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "istio-ingressgateway",
+      "namespace": "istio-system",
+      "ports": {
+        "15021": 15021,
+        "443": 8443,
+        "80": 8080
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.6.94"]
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/istio-system/istiod-54b6586f84-9bgw2": {
+          "port": {
+            "15010": 15010,
+            "15012": 15012,
+            "15014": 15014,
+            "443": 15017
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/istio-system/istiod-54b6586f84-9bgw2"
+        }
+      },
+      "hostname": "istiod.istio-system.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "istiod",
+      "namespace": "istio-system",
+      "ports": {
+        "15010": 15010,
+        "15012": 15012,
+        "15014": 15014,
+        "443": 15017
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.140.151"]
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/istio-system/jaeger-d89bc58b9-d7tf2": {
+          "port": {
+            "14250": 14250,
+            "14268": 14268,
+            "4317": 4317,
+            "4318": 4318,
+            "9411": 9411
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/istio-system/jaeger-d89bc58b9-d7tf2"
+        }
+      },
+      "hostname": "jaeger-collector.istio-system.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "jaeger-collector",
+      "namespace": "istio-system",
+      "ports": {
+        "14250": 14250,
+        "14268": 14268,
+        "4317": 4317,
+        "4318": 4318,
+        "9411": 9411
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.16.215"]
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/keycloak/keycloak-0": {
+          "port": {
+            "8080": 8080,
+            "8443": 8443
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/keycloak/keycloak-0"
+        }
+      },
+      "hostname": "keycloak-headless.keycloak.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "loadBalancer": {
+        "healthPolicy": "AllowAll",
+        "mode": "Standard",
+        "routingPreferences": []
+      },
+      "name": "keycloak-headless",
+      "namespace": "keycloak",
+      "ports": {
+        "8080": 0,
+        "8443": 0
+      },
+      "subjectAltNames": [],
+      "vips": []
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/keycloak/keycloak-postgresql-0": {
+          "port": {
+            "5432": 5432
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/keycloak/keycloak-postgresql-0"
+        }
+      },
+      "hostname": "keycloak-postgresql-hl.keycloak.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "loadBalancer": {
+        "healthPolicy": "AllowAll",
+        "mode": "Standard",
+        "routingPreferences": []
+      },
+      "name": "keycloak-postgresql-hl",
+      "namespace": "keycloak",
+      "ports": {
+        "5432": 0
+      },
+      "subjectAltNames": [],
+      "vips": []
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/keycloak/keycloak-postgresql-0": {
+          "port": {
+            "5432": 5432
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/keycloak/keycloak-postgresql-0"
+        }
+      },
+      "hostname": "keycloak-postgresql.keycloak.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "keycloak-postgresql",
+      "namespace": "keycloak",
+      "ports": {
+        "5432": 0
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.61.11"]
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/keycloak/keycloak-0": {
+          "port": {
+            "443": 8443,
+            "80": 8080
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/keycloak/keycloak-0"
+        }
+      },
+      "hostname": "keycloak.keycloak.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "keycloak",
+      "namespace": "keycloak",
+      "ports": {
+        "443": 0,
+        "80": 0
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.213.239"]
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/istio-system/kiali-7898fcc67d-5b6xl": {
+          "port": {
+            "80": 20001,
+            "9090": 9090
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/istio-system/kiali-7898fcc67d-5b6xl"
+        }
+      },
+      "hostname": "kiali.istio-system.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "kiali",
+      "namespace": "istio-system",
+      "ports": {
+        "80": 20001,
+        "9090": 9090
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.119.35"]
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/kube-system/coredns-5d78c9869d-j7t9q": {
+          "port": {
+            "53": 53,
+            "9153": 9153
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/kube-system/coredns-5d78c9869d-j7t9q"
+        },
+        "cluster-default//Pod/kube-system/coredns-5d78c9869d-t7vcc": {
+          "port": {
+            "53": 53,
+            "9153": 9153
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/kube-system/coredns-5d78c9869d-t7vcc"
+        }
+      },
+      "hostname": "kube-dns.kube-system.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "kube-dns",
+      "namespace": "kube-system",
+      "ports": {
+        "53": 53,
+        "9153": 9153
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.0.10"]
+    },
+    {
+      "endpoints": {
+        "cluster-default/discovery.k8s.io/EndpointSlice/default/kubernetes/172.18.0.5": {
+          "port": {
+            "443": 6443
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default/discovery.k8s.io/EndpointSlice/default/kubernetes/172.18.0.5"
+        }
+      },
+      "hostname": "kubernetes.default.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "kubernetes",
+      "namespace": "default",
+      "ports": {
+        "443": 6443
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.0.1"]
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/travel-agency/mysqldb-v1-bd877d7fc-6hntn": {
+          "port": {
+            "3306": 3306
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/travel-agency/mysqldb-v1-bd877d7fc-6hntn"
+        }
+      },
+      "hostname": "mysqldb.travel-agency.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "mysqldb",
+      "namespace": "travel-agency",
+      "ports": {
+        "3306": 3306
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.45.104"]
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/bookinfo/productpage-v1-f47f868c8-vlj4p": {
+          "port": {
+            "9080": 9080
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/bookinfo/productpage-v1-f47f868c8-vlj4p"
+        }
+      },
+      "hostname": "productpage-v1.bookinfo.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "productpage-v1",
+      "namespace": "bookinfo",
+      "ports": {
+        "9080": 9080
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.83.103"],
+      "waypoint": {
+        "destination": "bookinfo/waypoint.bookinfo.svc.cluster.local",
+        "hboneMtlsPort": 15008
+      }
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/bookinfo/productpage-v1-f47f868c8-vlj4p": {
+          "port": {
+            "9080": 9080
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/bookinfo/productpage-v1-f47f868c8-vlj4p"
+        }
+      },
+      "hostname": "productpage.bookinfo.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "productpage",
+      "namespace": "bookinfo",
+      "ports": {
+        "9080": 9080
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.209.124"],
+      "waypoint": {
+        "destination": "bookinfo/waypoint.bookinfo.svc.cluster.local",
+        "hboneMtlsPort": 15008
+      }
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/istio-system/prometheus-86f99d868-w247r": {
+          "port": {
+            "9090": 9090
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/istio-system/prometheus-86f99d868-w247r"
+        }
+      },
+      "hostname": "prometheus.istio-system.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "prometheus",
+      "namespace": "istio-system",
+      "ports": {
+        "9090": 9090
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.143.95"]
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/bookinfo/ratings-v1-85cf8d8647-gtptp": {
+          "port": {
+            "9080": 9080
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/bookinfo/ratings-v1-85cf8d8647-gtptp"
+        }
+      },
+      "hostname": "ratings-v1.bookinfo.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "ratings-v1",
+      "namespace": "bookinfo",
+      "ports": {
+        "9080": 9080
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.93.254"],
+      "waypoint": {
+        "destination": "bookinfo/waypoint.bookinfo.svc.cluster.local",
+        "hboneMtlsPort": 15008
+      }
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/bookinfo/ratings-v1-85cf8d8647-gtptp": {
+          "port": {
+            "9080": 9080
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/bookinfo/ratings-v1-85cf8d8647-gtptp"
+        }
+      },
+      "hostname": "ratings.bookinfo.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "ratings",
+      "namespace": "bookinfo",
+      "ports": {
+        "9080": 9080
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.237.201"],
+      "waypoint": {
+        "destination": "bookinfo/waypoint.bookinfo.svc.cluster.local",
+        "hboneMtlsPort": 15008
+      }
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/bookinfo/reviews-v1-5fc87d67c-r67vp": {
+          "port": {
+            "9080": 9080
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/bookinfo/reviews-v1-5fc87d67c-r67vp"
+        }
+      },
+      "hostname": "reviews-v1.bookinfo.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "reviews-v1",
+      "namespace": "bookinfo",
+      "ports": {
+        "9080": 9080
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.59.186"],
+      "waypoint": {
+        "destination": "bookinfo/waypoint.bookinfo.svc.cluster.local",
+        "hboneMtlsPort": 15008
+      }
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/bookinfo/reviews-v2-f6d449f65-v2qx7": {
+          "port": {
+            "9080": 9080
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/bookinfo/reviews-v2-f6d449f65-v2qx7"
+        }
+      },
+      "hostname": "reviews-v2.bookinfo.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "reviews-v2",
+      "namespace": "bookinfo",
+      "ports": {
+        "9080": 9080
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.81.236"],
+      "waypoint": {
+        "destination": "bookinfo/waypoint.bookinfo.svc.cluster.local",
+        "hboneMtlsPort": 15008
+      }
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/bookinfo/reviews-v3-76f75877b9-g6ds7": {
+          "port": {
+            "9080": 9080
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/bookinfo/reviews-v3-76f75877b9-g6ds7"
+        }
+      },
+      "hostname": "reviews-v3.bookinfo.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "reviews-v3",
+      "namespace": "bookinfo",
+      "ports": {
+        "9080": 9080
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.146.65"],
+      "waypoint": {
+        "destination": "bookinfo/waypoint.bookinfo.svc.cluster.local",
+        "hboneMtlsPort": 15008
+      }
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/bookinfo/reviews-v1-5fc87d67c-r67vp": {
+          "port": {
+            "9080": 9080
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/bookinfo/reviews-v1-5fc87d67c-r67vp"
+        },
+        "cluster-default//Pod/bookinfo/reviews-v2-f6d449f65-v2qx7": {
+          "port": {
+            "9080": 9080
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/bookinfo/reviews-v2-f6d449f65-v2qx7"
+        },
+        "cluster-default//Pod/bookinfo/reviews-v3-76f75877b9-g6ds7": {
+          "port": {
+            "9080": 9080
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/bookinfo/reviews-v3-76f75877b9-g6ds7"
+        }
+      },
+      "hostname": "reviews.bookinfo.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "reviews",
+      "namespace": "bookinfo",
+      "ports": {
+        "9080": 9080
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.224.224"],
+      "waypoint": {
+        "destination": "bookinfo/waypoint.bookinfo.svc.cluster.local",
+        "hboneMtlsPort": 15008
+      }
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/istio-system/jaeger-d89bc58b9-d7tf2": {
+          "port": {
+            "16685": 16685,
+            "80": 16686
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/istio-system/jaeger-d89bc58b9-d7tf2"
+        }
+      },
+      "hostname": "tracing.istio-system.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "tracing",
+      "namespace": "istio-system",
+      "ports": {
+        "16685": 16685,
+        "80": 16686
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.42.73"]
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/travel-agency/travels-v1-5cb87c8dbd-h4cjs": {
+          "port": {
+            "8000": 8000
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/travel-agency/travels-v1-5cb87c8dbd-h4cjs"
+        }
+      },
+      "hostname": "travels.travel-agency.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "travels",
+      "namespace": "travel-agency",
+      "ports": {
+        "8000": 8000
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.157.40"]
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/travel-portal/travels-659bd87ccd-9s9bw": {
+          "port": {
+            "8000": 8000
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/travel-portal/travels-659bd87ccd-9s9bw"
+        }
+      },
+      "hostname": "travels.travel-portal.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "travels",
+      "namespace": "travel-portal",
+      "ports": {
+        "8000": 8000
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.108.253"]
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/travel-portal/viaggi-74c7c5f946-6gh5k": {
+          "port": {
+            "8000": 8000
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/travel-portal/viaggi-74c7c5f946-6gh5k"
+        }
+      },
+      "hostname": "viaggi.travel-portal.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "viaggi",
+      "namespace": "travel-portal",
+      "ports": {
+        "8000": 8000
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.247.142"]
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/travel-portal/voyages-5b4f7d7d75-dz626": {
+          "port": {
+            "8000": 8000
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/travel-portal/voyages-5b4f7d7d75-dz626"
+        }
+      },
+      "hostname": "voyages.travel-portal.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "voyages",
+      "namespace": "travel-portal",
+      "ports": {
+        "8000": 8000
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.97.150"]
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/bookinfo/waypoint-7564f99fc9-xlkjz": {
+          "port": {
+            "15008": 15008,
+            "15021": 15021
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/bookinfo/waypoint-7564f99fc9-xlkjz"
+        }
+      },
+      "hostname": "waypoint.bookinfo.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "loadBalancer": {
+        "healthPolicy": "OnlyHealthy",
+        "mode": "Failover",
+        "routingPreferences": ["Network", "Region", "Zone", "Subzone"]
+      },
+      "name": "waypoint",
+      "namespace": "bookinfo",
+      "ports": {
+        "15008": 15008,
+        "15021": 15021
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.213.240"]
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/metallb-system/controller-595f88d88f-hxf64": {
+          "port": {
+            "443": 9443
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/metallb-system/controller-595f88d88f-hxf64"
+        }
+      },
+      "hostname": "webhook-service.metallb-system.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "webhook-service",
+      "namespace": "metallb-system",
+      "ports": {
+        "443": 9443
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.154.160"]
+    },
+    {
+      "endpoints": {
+        "cluster-default//Pod/istio-system/jaeger-d89bc58b9-d7tf2": {
+          "port": {
+            "9411": 9411
+          },
+          "status": "Healthy",
+          "workloadUid": "cluster-default//Pod/istio-system/jaeger-d89bc58b9-d7tf2"
+        }
+      },
+      "hostname": "zipkin.istio-system.svc.cluster.local",
+      "ipFamilies": "IPv4",
+      "name": "zipkin",
+      "namespace": "istio-system",
+      "ports": {
+        "9411": 9411
+      },
+      "subjectAltNames": [],
+      "vips": ["/10.96.82.57"]
+    }
+  ],
+  "stagedServices": {},
+  "staticConfig": {
+    "policies": [],
+    "services": [],
+    "workloads": []
+  },
+  "version": {
+    "buildProfile": "release",
+    "buildStatus": "Clean",
+    "gitRevision": "4c7cdf1b62ddcc786402499c03eff0d5172c95ef",
+    "gitTag": "1.24.0-2-g4c7cdf1",
+    "istioVersion": "unknown",
+    "rustVersion": "1.81.0",
+    "version": "4c7cdf1b62ddcc786402499c03eff0d5172c95ef"
+  },
+  "workloadState": {},
+  "workloads": [
+    {
+      "canonicalName": "bookinfo-gateway-istio",
+      "canonicalRevision": "latest",
+      "clusterId": "cluster-default",
+      "name": "bookinfo-gateway-istio-7c875f844-dllvg",
+      "namespace": "bookinfo",
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "TCP",
+      "serviceAccount": "bookinfo-gateway-istio",
+      "services": [
+        "bookinfo/bookinfo-gateway-istio.bookinfo.svc.cluster.local"
+      ],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/bookinfo/bookinfo-gateway-istio-7c875f844-dllvg",
+      "workloadIps": ["10.244.1.22"],
+      "workloadName": "bookinfo-gateway-istio",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "details",
+      "canonicalRevision": "v1",
+      "clusterId": "cluster-default",
+      "name": "details-v1-7c5d957895-pwqdh",
+      "namespace": "bookinfo",
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "HBONE",
+      "serviceAccount": "bookinfo-details",
+      "services": [
+        "bookinfo/details.bookinfo.svc.cluster.local",
+        "bookinfo/details-v1.bookinfo.svc.cluster.local"
+      ],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/bookinfo/details-v1-7c5d957895-pwqdh",
+      "workloadIps": ["10.244.1.15"],
+      "workloadName": "details-v1",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "kiali-traffic-generator",
+      "canonicalRevision": "latest",
+      "clusterId": "cluster-default",
+      "name": "kiali-traffic-generator-dvxz2",
+      "namespace": "bookinfo",
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "HBONE",
+      "serviceAccount": "default",
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/bookinfo/kiali-traffic-generator-dvxz2",
+      "workloadIps": ["10.244.1.24"],
+      "workloadName": "kiali-traffic-generator-dvxz2",
+      "workloadType": "pod"
+    },
+    {
+      "canonicalName": "productpage",
+      "canonicalRevision": "v1",
+      "clusterId": "cluster-default",
+      "name": "productpage-v1-f47f868c8-vlj4p",
+      "namespace": "bookinfo",
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "HBONE",
+      "serviceAccount": "bookinfo-productpage",
+      "services": [
+        "bookinfo/productpage.bookinfo.svc.cluster.local",
+        "bookinfo/productpage-v1.bookinfo.svc.cluster.local"
+      ],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/bookinfo/productpage-v1-f47f868c8-vlj4p",
+      "workloadIps": ["10.244.1.20"],
+      "workloadName": "productpage-v1",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "ratings",
+      "canonicalRevision": "v1",
+      "clusterId": "cluster-default",
+      "name": "ratings-v1-85cf8d8647-gtptp",
+      "namespace": "bookinfo",
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "HBONE",
+      "serviceAccount": "bookinfo-ratings",
+      "services": [
+        "bookinfo/ratings.bookinfo.svc.cluster.local",
+        "bookinfo/ratings-v1.bookinfo.svc.cluster.local"
+      ],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/bookinfo/ratings-v1-85cf8d8647-gtptp",
+      "workloadIps": ["10.244.1.16"],
+      "workloadName": "ratings-v1",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "reviews",
+      "canonicalRevision": "v1",
+      "clusterId": "cluster-default",
+      "name": "reviews-v1-5fc87d67c-r67vp",
+      "namespace": "bookinfo",
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "HBONE",
+      "serviceAccount": "bookinfo-reviews",
+      "services": [
+        "bookinfo/reviews.bookinfo.svc.cluster.local",
+        "bookinfo/reviews-v1.bookinfo.svc.cluster.local"
+      ],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/bookinfo/reviews-v1-5fc87d67c-r67vp",
+      "workloadIps": ["10.244.1.17"],
+      "workloadName": "reviews-v1",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "reviews",
+      "canonicalRevision": "v2",
+      "clusterId": "cluster-default",
+      "name": "reviews-v2-f6d449f65-v2qx7",
+      "namespace": "bookinfo",
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "HBONE",
+      "serviceAccount": "bookinfo-reviews",
+      "services": [
+        "bookinfo/reviews.bookinfo.svc.cluster.local",
+        "bookinfo/reviews-v2.bookinfo.svc.cluster.local"
+      ],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/bookinfo/reviews-v2-f6d449f65-v2qx7",
+      "workloadIps": ["10.244.1.18"],
+      "workloadName": "reviews-v2",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "reviews",
+      "canonicalRevision": "v3",
+      "clusterId": "cluster-default",
+      "name": "reviews-v3-76f75877b9-g6ds7",
+      "namespace": "bookinfo",
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "HBONE",
+      "serviceAccount": "bookinfo-reviews",
+      "services": [
+        "bookinfo/reviews-v3.bookinfo.svc.cluster.local",
+        "bookinfo/reviews.bookinfo.svc.cluster.local"
+      ],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/bookinfo/reviews-v3-76f75877b9-g6ds7",
+      "workloadIps": ["10.244.1.19"],
+      "workloadName": "reviews-v3",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "waypoint",
+      "canonicalRevision": "latest",
+      "clusterId": "cluster-default",
+      "name": "waypoint-7564f99fc9-xlkjz",
+      "namespace": "bookinfo",
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "TCP",
+      "serviceAccount": "waypoint",
+      "services": ["bookinfo/waypoint.bookinfo.svc.cluster.local"],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/bookinfo/waypoint-7564f99fc9-xlkjz",
+      "workloadIps": ["10.244.1.23"],
+      "workloadName": "waypoint",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "grafana",
+      "canonicalRevision": "latest",
+      "clusterId": "cluster-default",
+      "name": "grafana-7874f88995-49vm4",
+      "namespace": "istio-system",
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "TCP",
+      "serviceAccount": "grafana",
+      "services": ["istio-system/grafana.istio-system.svc.cluster.local"],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/istio-system/grafana-7874f88995-49vm4",
+      "workloadIps": ["10.244.1.11"],
+      "workloadName": "grafana",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "istio-cni",
+      "canonicalRevision": "1.24.1",
+      "clusterId": "cluster-default",
+      "name": "istio-cni-node-gn8px",
+      "namespace": "istio-system",
+      "networkMode": "HostNetwork",
+      "node": "ci-control-plane",
+      "protocol": "TCP",
+      "serviceAccount": "istio-cni",
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/istio-system/istio-cni-node-gn8px",
+      "workloadIps": ["172.18.0.5"],
+      "workloadName": "istio-cni-node-gn8px",
+      "workloadType": "pod"
+    },
+    {
+      "canonicalName": "istio-cni",
+      "canonicalRevision": "1.24.1",
+      "clusterId": "cluster-default",
+      "name": "istio-cni-node-m287m",
+      "namespace": "istio-system",
+      "networkMode": "HostNetwork",
+      "node": "ci-worker",
+      "protocol": "TCP",
+      "serviceAccount": "istio-cni",
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/istio-system/istio-cni-node-m287m",
+      "workloadIps": ["172.18.0.6"],
+      "workloadName": "istio-cni-node-m287m",
+      "workloadType": "pod"
+    },
+    {
+      "canonicalName": "istio-egressgateway",
+      "canonicalRevision": "latest",
+      "clusterId": "cluster-default",
+      "name": "istio-egressgateway-66db7db9d4-kjcmw",
+      "namespace": "istio-system",
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "TCP",
+      "serviceAccount": "istio-egressgateway-service-account",
+      "services": [
+        "istio-system/istio-egressgateway.istio-system.svc.cluster.local"
+      ],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/istio-system/istio-egressgateway-66db7db9d4-kjcmw",
+      "workloadIps": ["10.244.1.7"],
+      "workloadName": "istio-egressgateway",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "istio-ingressgateway",
+      "canonicalRevision": "latest",
+      "clusterId": "cluster-default",
+      "name": "istio-ingressgateway-74d79d79fc-q6x6q",
+      "namespace": "istio-system",
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "TCP",
+      "serviceAccount": "istio-ingressgateway-service-account",
+      "services": [
+        "istio-system/istio-ingressgateway.istio-system.svc.cluster.local"
+      ],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/istio-system/istio-ingressgateway-74d79d79fc-q6x6q",
+      "workloadIps": ["10.244.1.8"],
+      "workloadName": "istio-ingressgateway",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "istiod",
+      "canonicalRevision": "1.24.1",
+      "clusterId": "cluster-default",
+      "name": "istiod-54b6586f84-9bgw2",
+      "namespace": "istio-system",
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "TCP",
+      "serviceAccount": "istiod",
+      "services": ["istio-system/istiod.istio-system.svc.cluster.local"],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/istio-system/istiod-54b6586f84-9bgw2",
+      "workloadIps": ["10.244.1.6"],
+      "workloadName": "istiod",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "jaeger",
+      "canonicalRevision": "latest",
+      "clusterId": "cluster-default",
+      "name": "jaeger-d89bc58b9-d7tf2",
+      "namespace": "istio-system",
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "TCP",
+      "serviceAccount": "default",
+      "services": [
+        "istio-system/jaeger-collector.istio-system.svc.cluster.local",
+        "istio-system/tracing.istio-system.svc.cluster.local",
+        "istio-system/zipkin.istio-system.svc.cluster.local"
+      ],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/istio-system/jaeger-d89bc58b9-d7tf2",
+      "workloadIps": ["10.244.1.12"],
+      "workloadName": "jaeger",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "kiali",
+      "canonicalRevision": "v2.3.0-SNAPSHOT",
+      "clusterId": "cluster-default",
+      "name": "kiali-7898fcc67d-5b6xl",
+      "namespace": "istio-system",
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "TCP",
+      "serviceAccount": "kiali",
+      "services": ["istio-system/kiali.istio-system.svc.cluster.local"],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/istio-system/kiali-7898fcc67d-5b6xl",
+      "workloadIps": ["10.244.1.27"],
+      "workloadName": "kiali",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "prometheus",
+      "canonicalRevision": "v2.54.1",
+      "clusterId": "cluster-default",
+      "name": "prometheus-86f99d868-w247r",
+      "namespace": "istio-system",
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "TCP",
+      "serviceAccount": "prometheus",
+      "services": ["istio-system/prometheus.istio-system.svc.cluster.local"],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/istio-system/prometheus-86f99d868-w247r",
+      "workloadIps": ["10.244.1.10"],
+      "workloadName": "prometheus",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "ztunnel",
+      "canonicalRevision": "1.24.1",
+      "clusterId": "cluster-default",
+      "name": "ztunnel-6z6nt",
+      "namespace": "istio-system",
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "TCP",
+      "serviceAccount": "ztunnel",
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/istio-system/ztunnel-6z6nt",
+      "workloadIps": ["10.244.1.9"],
+      "workloadName": "ztunnel-6z6nt",
+      "workloadType": "pod"
+    },
+    {
+      "canonicalName": "ztunnel",
+      "canonicalRevision": "1.24.1",
+      "clusterId": "cluster-default",
+      "name": "ztunnel-7hml8",
+      "namespace": "istio-system",
+      "networkMode": "Standard",
+      "node": "ci-control-plane",
+      "protocol": "TCP",
+      "serviceAccount": "ztunnel",
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/istio-system/ztunnel-7hml8",
+      "workloadIps": ["10.244.0.5"],
+      "workloadName": "ztunnel-7hml8",
+      "workloadType": "pod"
+    },
+    {
+      "canonicalName": "keycloak",
+      "canonicalRevision": "26.0.7",
+      "clusterId": "cluster-default",
+      "name": "keycloak-0",
+      "namespace": "keycloak",
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "TCP",
+      "serviceAccount": "keycloak",
+      "services": [
+        "keycloak/keycloak-headless.keycloak.svc.cluster.local",
+        "keycloak/keycloak.keycloak.svc.cluster.local"
+      ],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/keycloak/keycloak-0",
+      "workloadIps": ["10.244.1.3"],
+      "workloadName": "keycloak-0",
+      "workloadType": "pod"
+    },
+    {
+      "canonicalName": "postgresql",
+      "canonicalRevision": "17.2.0",
+      "clusterId": "cluster-default",
+      "name": "keycloak-postgresql-0",
+      "namespace": "keycloak",
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "TCP",
+      "serviceAccount": "keycloak-postgresql",
+      "services": [
+        "keycloak/keycloak-postgresql.keycloak.svc.cluster.local",
+        "keycloak/keycloak-postgresql-hl.keycloak.svc.cluster.local"
+      ],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/keycloak/keycloak-postgresql-0",
+      "workloadIps": ["10.244.1.5"],
+      "workloadName": "keycloak-postgresql-0",
+      "workloadType": "pod"
+    },
+    {
+      "canonicalName": "coredns",
+      "canonicalRevision": "latest",
+      "clusterId": "cluster-default",
+      "name": "coredns-5d78c9869d-j7t9q",
+      "namespace": "kube-system",
+      "networkMode": "Standard",
+      "node": "ci-control-plane",
+      "protocol": "TCP",
+      "serviceAccount": "coredns",
+      "services": ["kube-system/kube-dns.kube-system.svc.cluster.local"],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/kube-system/coredns-5d78c9869d-j7t9q",
+      "workloadIps": ["10.244.0.3"],
+      "workloadName": "coredns",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "coredns",
+      "canonicalRevision": "latest",
+      "clusterId": "cluster-default",
+      "name": "coredns-5d78c9869d-t7vcc",
+      "namespace": "kube-system",
+      "networkMode": "Standard",
+      "node": "ci-control-plane",
+      "protocol": "TCP",
+      "serviceAccount": "coredns",
+      "services": ["kube-system/kube-dns.kube-system.svc.cluster.local"],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/kube-system/coredns-5d78c9869d-t7vcc",
+      "workloadIps": ["10.244.0.2"],
+      "workloadName": "coredns",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "etcd-ci-control-plane",
+      "canonicalRevision": "latest",
+      "clusterId": "cluster-default",
+      "name": "etcd-ci-control-plane",
+      "namespace": "kube-system",
+      "networkMode": "HostNetwork",
+      "node": "ci-control-plane",
+      "protocol": "TCP",
+      "serviceAccount": "default",
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/kube-system/etcd-ci-control-plane",
+      "workloadIps": ["172.18.0.5"],
+      "workloadName": "etcd-ci-control-plane",
+      "workloadType": "pod"
+    },
+    {
+      "canonicalName": "kindnet",
+      "canonicalRevision": "latest",
+      "clusterId": "cluster-default",
+      "name": "kindnet-9c5wr",
+      "namespace": "kube-system",
+      "networkMode": "HostNetwork",
+      "node": "ci-control-plane",
+      "protocol": "TCP",
+      "serviceAccount": "kindnet",
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/kube-system/kindnet-9c5wr",
+      "workloadIps": ["172.18.0.5"],
+      "workloadName": "kindnet-9c5wr",
+      "workloadType": "pod"
+    },
+    {
+      "canonicalName": "kindnet",
+      "canonicalRevision": "latest",
+      "clusterId": "cluster-default",
+      "name": "kindnet-f4c5q",
+      "namespace": "kube-system",
+      "networkMode": "HostNetwork",
+      "node": "ci-worker",
+      "protocol": "TCP",
+      "serviceAccount": "kindnet",
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/kube-system/kindnet-f4c5q",
+      "workloadIps": ["172.18.0.6"],
+      "workloadName": "kindnet-f4c5q",
+      "workloadType": "pod"
+    },
+    {
+      "canonicalName": "kube-apiserver-ci-control-plane",
+      "canonicalRevision": "latest",
+      "clusterId": "cluster-default",
+      "name": "kube-apiserver-ci-control-plane",
+      "namespace": "kube-system",
+      "networkMode": "HostNetwork",
+      "node": "ci-control-plane",
+      "protocol": "TCP",
+      "serviceAccount": "default",
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/kube-system/kube-apiserver-ci-control-plane",
+      "workloadIps": ["172.18.0.5"],
+      "workloadName": "kube-apiserver-ci-control-plane",
+      "workloadType": "pod"
+    },
+    {
+      "canonicalName": "kube-controller-manager-ci-control-plane",
+      "canonicalRevision": "latest",
+      "clusterId": "cluster-default",
+      "name": "kube-controller-manager-ci-control-plane",
+      "namespace": "kube-system",
+      "networkMode": "HostNetwork",
+      "node": "ci-control-plane",
+      "protocol": "TCP",
+      "serviceAccount": "default",
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/kube-system/kube-controller-manager-ci-control-plane",
+      "workloadIps": ["172.18.0.5"],
+      "workloadName": "kube-controller-manager-ci-control-plane",
+      "workloadType": "pod"
+    },
+    {
+      "canonicalName": "kube-proxy-dsmfj",
+      "canonicalRevision": "latest",
+      "clusterId": "cluster-default",
+      "name": "kube-proxy-dsmfj",
+      "namespace": "kube-system",
+      "networkMode": "HostNetwork",
+      "node": "ci-worker",
+      "protocol": "TCP",
+      "serviceAccount": "kube-proxy",
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/kube-system/kube-proxy-dsmfj",
+      "workloadIps": ["172.18.0.6"],
+      "workloadName": "kube-proxy-dsmfj",
+      "workloadType": "pod"
+    },
+    {
+      "canonicalName": "kube-proxy-mh2lk",
+      "canonicalRevision": "latest",
+      "clusterId": "cluster-default",
+      "name": "kube-proxy-mh2lk",
+      "namespace": "kube-system",
+      "networkMode": "HostNetwork",
+      "node": "ci-control-plane",
+      "protocol": "TCP",
+      "serviceAccount": "kube-proxy",
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/kube-system/kube-proxy-mh2lk",
+      "workloadIps": ["172.18.0.5"],
+      "workloadName": "kube-proxy-mh2lk",
+      "workloadType": "pod"
+    },
+    {
+      "canonicalName": "kube-scheduler-ci-control-plane",
+      "canonicalRevision": "latest",
+      "clusterId": "cluster-default",
+      "name": "kube-scheduler-ci-control-plane",
+      "namespace": "kube-system",
+      "networkMode": "HostNetwork",
+      "node": "ci-control-plane",
+      "protocol": "TCP",
+      "serviceAccount": "default",
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/kube-system/kube-scheduler-ci-control-plane",
+      "workloadIps": ["172.18.0.5"],
+      "workloadName": "kube-scheduler-ci-control-plane",
+      "workloadType": "pod"
+    },
+    {
+      "canonicalName": "local-path-provisioner",
+      "canonicalRevision": "latest",
+      "clusterId": "cluster-default",
+      "name": "local-path-provisioner-6bc4bddd6b-8jtdf",
+      "namespace": "local-path-storage",
+      "networkMode": "Standard",
+      "node": "ci-control-plane",
+      "protocol": "TCP",
+      "serviceAccount": "local-path-provisioner-service-account",
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/local-path-storage/local-path-provisioner-6bc4bddd6b-8jtdf",
+      "workloadIps": ["10.244.0.4"],
+      "workloadName": "local-path-provisioner",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "metallb",
+      "canonicalRevision": "latest",
+      "clusterId": "cluster-default",
+      "name": "controller-595f88d88f-hxf64",
+      "namespace": "metallb-system",
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "TCP",
+      "serviceAccount": "controller",
+      "services": [
+        "metallb-system/webhook-service.metallb-system.svc.cluster.local"
+      ],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/metallb-system/controller-595f88d88f-hxf64",
+      "workloadIps": ["10.244.1.2"],
+      "workloadName": "controller",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "metallb",
+      "canonicalRevision": "latest",
+      "clusterId": "cluster-default",
+      "name": "speaker-8c8nw",
+      "namespace": "metallb-system",
+      "networkMode": "HostNetwork",
+      "node": "ci-worker",
+      "protocol": "TCP",
+      "serviceAccount": "speaker",
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/metallb-system/speaker-8c8nw",
+      "workloadIps": ["172.18.0.6"],
+      "workloadName": "speaker-8c8nw",
+      "workloadType": "pod"
+    },
+    {
+      "canonicalName": "metallb",
+      "canonicalRevision": "latest",
+      "clusterId": "cluster-default",
+      "name": "speaker-pmxbn",
+      "namespace": "metallb-system",
+      "networkMode": "HostNetwork",
+      "node": "ci-control-plane",
+      "protocol": "TCP",
+      "serviceAccount": "speaker",
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/metallb-system/speaker-pmxbn",
+      "workloadIps": ["172.18.0.5"],
+      "workloadName": "speaker-pmxbn",
+      "workloadType": "pod"
+    },
+    {
+      "canonicalName": "cars",
+      "canonicalRevision": "v1",
+      "clusterId": "cluster-default",
+      "name": "cars-v1-8f6f65b75-f62nc",
+      "namespace": "travel-agency",
+      "nativeTunnel": true,
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "HBONE",
+      "serviceAccount": "default",
+      "services": ["travel-agency/cars.travel-agency.svc.cluster.local"],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/travel-agency/cars-v1-8f6f65b75-f62nc",
+      "workloadIps": ["10.244.1.29"],
+      "workloadName": "cars-v1",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "discounts",
+      "canonicalRevision": "v1",
+      "clusterId": "cluster-default",
+      "name": "discounts-v1-58c9676688-sx2vp",
+      "namespace": "travel-agency",
+      "nativeTunnel": true,
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "HBONE",
+      "serviceAccount": "default",
+      "services": ["travel-agency/discounts.travel-agency.svc.cluster.local"],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/travel-agency/discounts-v1-58c9676688-sx2vp",
+      "workloadIps": ["10.244.1.30"],
+      "workloadName": "discounts-v1",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "flights",
+      "canonicalRevision": "v1",
+      "clusterId": "cluster-default",
+      "name": "flights-v1-6775bb778f-d88gr",
+      "namespace": "travel-agency",
+      "nativeTunnel": true,
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "HBONE",
+      "serviceAccount": "default",
+      "services": ["travel-agency/flights.travel-agency.svc.cluster.local"],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/travel-agency/flights-v1-6775bb778f-d88gr",
+      "workloadIps": ["10.244.1.31"],
+      "workloadName": "flights-v1",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "hotels",
+      "canonicalRevision": "v1",
+      "clusterId": "cluster-default",
+      "name": "hotels-v1-5799fbd56c-bwpsb",
+      "namespace": "travel-agency",
+      "nativeTunnel": true,
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "HBONE",
+      "serviceAccount": "default",
+      "services": ["travel-agency/hotels.travel-agency.svc.cluster.local"],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/travel-agency/hotels-v1-5799fbd56c-bwpsb",
+      "workloadIps": ["10.244.1.32"],
+      "workloadName": "hotels-v1",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "insurances",
+      "canonicalRevision": "v1",
+      "clusterId": "cluster-default",
+      "name": "insurances-v1-96d96c758-qs6sn",
+      "namespace": "travel-agency",
+      "nativeTunnel": true,
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "HBONE",
+      "serviceAccount": "default",
+      "services": ["travel-agency/insurances.travel-agency.svc.cluster.local"],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/travel-agency/insurances-v1-96d96c758-qs6sn",
+      "workloadIps": ["10.244.1.33"],
+      "workloadName": "insurances-v1",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "mysqldb",
+      "canonicalRevision": "v1",
+      "clusterId": "cluster-default",
+      "name": "mysqldb-v1-bd877d7fc-6hntn",
+      "namespace": "travel-agency",
+      "nativeTunnel": true,
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "HBONE",
+      "serviceAccount": "default",
+      "services": ["travel-agency/mysqldb.travel-agency.svc.cluster.local"],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/travel-agency/mysqldb-v1-bd877d7fc-6hntn",
+      "workloadIps": ["10.244.1.28"],
+      "workloadName": "mysqldb-v1",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "travels",
+      "canonicalRevision": "v1",
+      "clusterId": "cluster-default",
+      "name": "travels-v1-5cb87c8dbd-h4cjs",
+      "namespace": "travel-agency",
+      "nativeTunnel": true,
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "HBONE",
+      "serviceAccount": "default",
+      "services": ["travel-agency/travels.travel-agency.svc.cluster.local"],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/travel-agency/travels-v1-5cb87c8dbd-h4cjs",
+      "workloadIps": ["10.244.1.34"],
+      "workloadName": "travels-v1",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "control",
+      "canonicalRevision": "v1",
+      "clusterId": "cluster-default",
+      "name": "control-57d5847d68-74kjs",
+      "namespace": "travel-control",
+      "nativeTunnel": true,
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "HBONE",
+      "serviceAccount": "default",
+      "services": ["travel-control/control.travel-control.svc.cluster.local"],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/travel-control/control-57d5847d68-74kjs",
+      "workloadIps": ["10.244.1.38"],
+      "workloadName": "control",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "travels",
+      "canonicalRevision": "v1",
+      "clusterId": "cluster-default",
+      "name": "travels-659bd87ccd-9s9bw",
+      "namespace": "travel-portal",
+      "nativeTunnel": true,
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "HBONE",
+      "serviceAccount": "default",
+      "services": ["travel-portal/travels.travel-portal.svc.cluster.local"],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/travel-portal/travels-659bd87ccd-9s9bw",
+      "workloadIps": ["10.244.1.37"],
+      "workloadName": "travels",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "viaggi",
+      "canonicalRevision": "v1",
+      "clusterId": "cluster-default",
+      "name": "viaggi-74c7c5f946-6gh5k",
+      "namespace": "travel-portal",
+      "nativeTunnel": true,
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "HBONE",
+      "serviceAccount": "default",
+      "services": ["travel-portal/viaggi.travel-portal.svc.cluster.local"],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/travel-portal/viaggi-74c7c5f946-6gh5k",
+      "workloadIps": ["10.244.1.36"],
+      "workloadName": "viaggi",
+      "workloadType": "deployment"
+    },
+    {
+      "canonicalName": "voyages",
+      "canonicalRevision": "v1",
+      "clusterId": "cluster-default",
+      "name": "voyages-5b4f7d7d75-dz626",
+      "namespace": "travel-portal",
+      "nativeTunnel": true,
+      "networkMode": "Standard",
+      "node": "ci-worker",
+      "protocol": "HBONE",
+      "serviceAccount": "default",
+      "services": ["travel-portal/voyages.travel-portal.svc.cluster.local"],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default//Pod/travel-portal/voyages-5b4f7d7d75-dz626",
+      "workloadIps": ["10.244.1.35"],
+      "workloadName": "voyages",
+      "workloadType": "deployment"
+    },
+    {
+      "clusterId": "cluster-default",
+      "name": "kubernetes",
+      "namespace": "default",
+      "networkMode": "HostNetwork",
+      "protocol": "TCP",
+      "serviceAccount": "default",
+      "services": ["default/kubernetes.default.svc.cluster.local"],
+      "status": "Healthy",
+      "trustDomain": "cluster.local",
+      "uid": "cluster-default/discovery.k8s.io/EndpointSlice/default/kubernetes/172.18.0.5",
+      "workloadIps": ["172.18.0.5"],
+      "workloadType": "deployment"
+    }
+  ]
+}
diff --git a/models/workload_test.go b/models/workload_test.go
index 0b070b6ee..81e05e0c1 100644
--- a/models/workload_test.go
+++ b/models/workload_test.go
@@ -165,6 +165,25 @@ func TestParsePodToWorkload(t *testing.T) {
 	assert.Equal("value-annot", w.AdditionalDetails[0].Value)
 }
 
+func TestParseWaypointPodToWorkload(t *testing.T) {
+	assert := assert.New(t)
+
+	w := Workload{}
+	w.ParsePod(fakeWaypointPod())
+
+	assert.Equal("waypoint", w.Name)
+	assert.Equal("istio.io-mesh-controller", w.Labels["gateway.istio.io/managed"])
+	assert.Equal("waypoint", w.Labels["gateway.networking.k8s.io/gateway-name"])
+	assert.Equal("2709198702082918", w.ResourceVersion)
+	assert.Equal("Pod", w.WorkloadGVK.Kind)
+	assert.False(w.IsAmbient)
+	assert.True(w.IsWaypoint())
+	assert.False(w.IsGateway())
+	assert.Equal(int32(1), w.DesiredReplicas)
+	assert.Equal(int32(1), w.CurrentReplicas)
+	assert.Equal(int32(1), w.AvailableReplicas)
+}
+
 func TestParsePodsToWorkload(t *testing.T) {
 	assert := assert.New(t)
 	config.Set(config.NewConfig())
@@ -409,3 +428,23 @@ func fakePod() *core_v1.Pod {
 		},
 	}
 }
+
+func fakeWaypointPod() *core_v1.Pod {
+	t1, _ := time.Parse(time.RFC822Z, "08 Mar 18 17:44 +0300")
+
+	return &core_v1.Pod{
+		TypeMeta: meta_v1.TypeMeta{
+			Kind: "Pod",
+		},
+		ObjectMeta: meta_v1.ObjectMeta{
+			Name:              "waypoint",
+			CreationTimestamp: meta_v1.NewTime(t1),
+			ResourceVersion:   "2709198702082918",
+			Labels:            map[string]string{"gateway.istio.io/managed": "istio.io-mesh-controller", "gateway.networking.k8s.io/gateway-name": "waypoint"},
+			Annotations:       map[string]string{"istio.io/rev": "default"},
+		},
+		Status: core_v1.PodStatus{
+			Phase: "Running",
+		},
+	}
+}
diff --git a/tests/integration/tests/graph_badges_test.go b/tests/integration/tests/graph_badges_test.go
index 7b512e390..5521f7e29 100644
--- a/tests/integration/tests/graph_badges_test.go
+++ b/tests/integration/tests/graph_badges_test.go
@@ -1,12 +1,12 @@
 package tests
 
 import (
+	"context"
 	"path"
 	"testing"
 	"time"
 
 	"github.com/stretchr/testify/require"
-	"golang.org/x/net/context"
 	"k8s.io/apimachinery/pkg/util/wait"
 
 	"github.com/kiali/kiali/log"
diff --git a/tests/integration/utils/kiali/kiali_client.go b/tests/integration/utils/kiali/kiali_client.go
index aa21d84b3..36fd8ca97 100644
--- a/tests/integration/utils/kiali/kiali_client.go
+++ b/tests/integration/utils/kiali/kiali_client.go
@@ -87,9 +87,11 @@ type MetricsJson struct {
 var client = *NewKialiClient()
 
 const (
-	BOOKINFO = "bookinfo"
-	ASSETS   = "tests/integration/assets"
-	TIMEOUT  = 10 * time.Second
+	BOOKINFO        = "bookinfo"
+	ASSETS          = "tests/integration/assets"
+	TIMEOUT         = 10 * time.Second
+	TIMEOUT_MEDIUM  = 20 * time.Second
+	TIMEOUT_TRACING = 60 * time.Second
 )
 
 func NewKialiClient() (c *KialiClient) {
@@ -268,7 +270,7 @@ func Traces(objectType, name, namespace string) (*model.TracingResponse, int, er
 	url := fmt.Sprintf("%s/api/namespaces/%s/%s/%s/traces?startMicros=%d&tags=&limit=100", client.kialiURL, namespace, objectType, name, TimeSince())
 	traces := new(model.TracingResponse)
 
-	code, err := getRequestAndUnmarshalInto(url, traces)
+	code, err := getRequestAndUnmarshalIntoWithCustomTimeout(url, TIMEOUT_TRACING, traces)
 	if err == nil {
 		return traces, code, nil
 	} else {
@@ -280,7 +282,7 @@ func Spans(objectType, name, namespace string) ([]model.TracingSpan, int, error)
 	url := fmt.Sprintf("%s/api/namespaces/%s/%s/%s/spans?startMicros=%d&tags=&limit=100", client.kialiURL, namespace, objectType, name, TimeSince())
 	spans := new([]model.TracingSpan)
 
-	code, err := getRequestAndUnmarshalInto(url, spans)
+	code, err := getRequestAndUnmarshalIntoWithCustomTimeout(url, TIMEOUT_TRACING, spans)
 	if err == nil {
 		return *spans, code, nil
 	} else {
@@ -504,7 +506,11 @@ func Grafana() (*models.GrafanaInfo, int, error) {
 }
 
 func getRequestAndUnmarshalInto[T any](url string, response *T) (int, error) {
-	body, code, _, err := httpGETWithRetry(url, client.GetAuth(), TIMEOUT, nil, nil)
+	return getRequestAndUnmarshalIntoWithCustomTimeout(url, TIMEOUT, response)
+}
+
+func getRequestAndUnmarshalIntoWithCustomTimeout[T any](url string, timeout time.Duration, response *T) (int, error) {
+	body, code, _, err := httpGETWithRetry(url, client.GetAuth(), timeout, nil, nil)
 	if err != nil {
 		return code, err
 	}
@@ -536,7 +542,7 @@ func IstioApiEnabled() (bool, error) {
 	url := fmt.Sprintf("%s/api/status", client.kialiURL)
 	status := new(status.StatusInfo)
 
-	_, err := getRequestAndUnmarshalInto(url, status)
+	_, err := getRequestAndUnmarshalIntoWithCustomTimeout(url, TIMEOUT_MEDIUM, status)
 	if err == nil {
 		return status.IstioEnvironment.IstioAPIEnabled, nil
 	} else {