Repository: kiali/kiali
Pull request #7993 (2024-12-12 11:26:03)

[hack] update CRC and provide a user network mode option
The name of the ssh identify file changed in recent versions of CRC - this updates the name (this allows us to ssh into the VM - this feature is used when obtaining status of the cluster, for example).

This also adds the option to enable user network mode in case you need to tell the VM to use the host's own networking.

And of course this also updates to the latest version of CRC.


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/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/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..4a08a0f70 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;
@@ -188,6 +189,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/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-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/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/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/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/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 {