Repository: linkerd/linkerd2
Pull request #6888 (2021-09-15 03:42:01)

policy: Support JSON log formatting
The policy controller only emitted logs in the default plain format.

This change adds new CLI flags to the policy-controller: `--log-format`
and `--log-level` that configure logging (replacing the `RUST_LOG`
environment variable). The helm chart is updated to configure these
flags--the `controllerLogLevel` variable is used to configure the policy
controller as well.

Example:

```
{"timestamp":"2021-09-15T03:30:49.552704Z","level":"INFO","fields":{"message":"HTTP admin server listening","addr":"0.0.0.0:8080"},"target":"linkerd_policy_controller::admin","spans":[{"addr":"0.0.0.0:8080","name":"serve"}]}
{"timestamp":"2021-09-15T03:30:49.552689Z","level":"INFO","fields":{"message":"gRPC server listening","addr":"0.0.0.0:8090"},"target":"linkerd_policy_controller","spans":[{"addr":"0.0.0.0:8090","cluster_networks":"[10.0.0.0/8, 100.64.0.0/10, 172.16.0.0/12, 192.168.0.0/16]","name":"grpc"}]}
{"timestamp":"2021-09-15T03:30:49.567734Z","level":"DEBUG","fields":{"message":"Ready"},"target":"linkerd_policy_controller_k8s_index"}
^C{"timestamp":"2021-09-15T03:30:51.245387Z","level":"DEBUG","fields":{"message":"Received ctrl-c"},"target":"linkerd_policy_controller"}
{"timestamp":"2021-09-15T03:30:51.245473Z","level":"INFO","fields":{"message":"Shutting down"},"target":"linkerd_policy_controller"}
```

<!--  Thanks for sending a pull request!

If you already have a well-structured git commit message, chances are GitHub
set the title and description of this PR to the git commit message subject and
body, respectively. If so, you may delete these instructions and submit your PR.

If this is your first time, please read our contributor guide:
https://github.com/linkerd/linkerd2/blob/main/CONTRIBUTING.md

The title and description of your Pull Request should match the git commit
subject and body, respectively. Git commit messages are structured as follows:

```
Subject

Problem

Solution

Validation

Fixes #[GitHub issue ID]

DCO Sign off
```

Example git commit message:

```
Introduce Pull Request Template

GitHub's community guidelines recommend a pull request template, the repo was
lacking one.

Introduce a `PULL_REQUEST_TEMPLATE.md` file.

Once merged, the
[Community profile checklist](https://github.com/linkerd/linkerd2/community)
should indicate the repo now provides a pull request template.

Fixes #3321

Signed-off-by: Jane Smith <jane.smith@example.com>
```

Note the git commit message subject becomes the pull request title.

For more details around git commits, see the section on Committing in our
contributor guide:
https://github.com/linkerd/linkerd2/blob/main/CONTRIBUTING.md#committing
-->



Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 535d9ecf7..c14cc5f9d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2049,6 +2049,16 @@ dependencies = [
  "tracing-core",
 ]
 
+[[package]]
+name = "tracing-serde"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b"
+dependencies = [
+ "serde",
+ "tracing-core",
+]
+
 [[package]]
 name = "tracing-subscriber"
 version = "0.2.22"
@@ -2058,11 +2068,15 @@ dependencies = [
  "lazy_static",
  "matchers",
  "regex",
+ "serde",
+ "serde_json",
  "sharded-slab",
+ "smallvec",
  "thread_local",
  "tracing",
  "tracing-core",
  "tracing-log",
+ "tracing-serde",
 ]
 
 [[package]]
diff --git a/charts/linkerd2/templates/destination.yaml b/charts/linkerd2/templates/destination.yaml
index fc253bac6..dd38c2d9d 100644
--- a/charts/linkerd2/templates/destination.yaml
+++ b/charts/linkerd2/templates/destination.yaml
@@ -255,9 +255,8 @@ spec:
         - --cluster-networks={{.Values.clusterNetworks}}
         - --identity-domain={{.Values.identityTrustDomain | default .Values.clusterDomain}}
         - --default-policy={{.Values.policyController.defaultAllowPolicy}}
-        env:
-        - name: RUST_LOG
-          value: {{.Values.policyController.logLevel | default "linkerd=info,warn" | quote}}
+        - --log-level={{.Values.policyController.logLevel | default "linkerd=info,warn"}}
+        - --log-format={{.Values.controllerLogFormat}}
         image: {{.Values.policyController.image.name}}:{{.Values.policyController.image.version | default .Values.linkerdVersion}}
         imagePullPolicy: {{.Values.policyController.image.pullPolicy | default .Values.imagePullPolicy}}
         livenessProbe:
diff --git a/controller/k8s/api.go b/controller/k8s/api.go
index 6adbe582e..82cf4556a 100644
--- a/controller/k8s/api.go
+++ b/controller/k8s/api.go
@@ -6,6 +6,7 @@ import (
 	"strings"
 	"time"
 
+	"k8s.io/client-go/dynamic"
 	"k8s.io/client-go/rest"
 
 	spv1alpha2 "github.com/linkerd/linkerd2/controller/gen/apis/serviceprofile/v1alpha2"
@@ -68,7 +69,8 @@ const (
 
 // API provides shared informers for all Kubernetes objects
 type API struct {
-	Client kubernetes.Interface
+	Client        kubernetes.Interface
+	DynamicClient dynamic.Interface
 
 	cj       batchv1beta1informers.CronJobInformer
 	cm       coreinformers.ConfigMapInformer
@@ -104,12 +106,17 @@ func InitializeAPI(ctx context.Context, kubeConfig string, ensureClusterWideAcce
 		return nil, fmt.Errorf("error configuring Kubernetes API client: %v", err)
 	}
 
+	dynamicClient, err := dynamic.NewForConfig(config)
+	if err != nil {
+		return nil, err
+	}
+
 	k8sClient, err := k8s.NewAPIForConfig(config, "", []string{}, 0)
 	if err != nil {
 		return nil, err
 	}
 
-	return initAPI(ctx, k8sClient, config, ensureClusterWideAccess, resources...)
+	return initAPI(ctx, k8sClient, dynamicClient, config, ensureClusterWideAccess, resources...)
 }
 
 // InitializeAPIForConfig creates Kubernetes clients and returns an initialized API wrapper.
@@ -119,10 +126,10 @@ func InitializeAPIForConfig(ctx context.Context, kubeConfig *rest.Config, ensure
 		return nil, err
 	}
 
-	return initAPI(ctx, k8sClient, kubeConfig, ensureClusterWideAccess, resources...)
+	return initAPI(ctx, k8sClient, nil, kubeConfig, ensureClusterWideAccess, resources...)
 }
 
-func initAPI(ctx context.Context, k8sClient *k8s.KubernetesAPI, kubeConfig *rest.Config, ensureClusterWideAccess bool, resources ...APIResource) (*API, error) {
+func initAPI(ctx context.Context, k8sClient *k8s.KubernetesAPI, dynamicClient dynamic.Interface, kubeConfig *rest.Config, ensureClusterWideAccess bool, resources ...APIResource) (*API, error) {
 	// check for cluster-wide access
 	var err error
 
@@ -164,7 +171,7 @@ func initAPI(ctx context.Context, k8sClient *k8s.KubernetesAPI, kubeConfig *rest
 		}
 	}
 
-	api := NewAPI(k8sClient, spClient, tsClient, resources...)
+	api := NewAPI(k8sClient, dynamicClient, spClient, tsClient, resources...)
 	for _, gauge := range api.gauges {
 		prometheus.Register(gauge)
 	}
@@ -174,6 +181,7 @@ func initAPI(ctx context.Context, k8sClient *k8s.KubernetesAPI, kubeConfig *rest
 // NewAPI takes a Kubernetes client and returns an initialized API.
 func NewAPI(
 	k8sClient kubernetes.Interface,
+	dynamicClient dynamic.Interface,
 	spClient spclient.Interface,
 	tsClient tsclient.Interface,
 	resources ...APIResource,
@@ -192,6 +200,7 @@ func NewAPI(
 
 	api := &API{
 		Client:            k8sClient,
+		DynamicClient:     dynamicClient,
 		syncChecks:        make([]cache.InformerSynced, 0),
 		sharedInformers:   sharedInformers,
 		spSharedInformers: spSharedInformers,
diff --git a/pkg/healthcheck/healthcheck.go b/pkg/healthcheck/healthcheck.go
index 16ee1893f..d6ffaa5ee 100644
--- a/pkg/healthcheck/healthcheck.go
+++ b/pkg/healthcheck/healthcheck.go
@@ -9,6 +9,7 @@ import (
 	"io"
 	"net"
 	"sort"
+	"strconv"
 	"strings"
 	"time"
 
@@ -20,6 +21,7 @@ import (
 	"github.com/linkerd/linkerd2/pkg/issuercerts"
 	"github.com/linkerd/linkerd2/pkg/k8s"
 	"github.com/linkerd/linkerd2/pkg/tls"
+	"github.com/linkerd/linkerd2/pkg/util"
 	"github.com/linkerd/linkerd2/pkg/version"
 	log "github.com/sirupsen/logrus"
 	admissionRegistration "k8s.io/api/admissionregistration/v1"
@@ -2216,7 +2218,7 @@ func (hc *HealthChecker) checkMisconfiguredOpaquePortAnnotations(ctx context.Con
 	// This is used instead of `hc.kubeAPI` to limit multiple k8s API requests
 	// and use the caching logic in the shared informers
 	// TODO: move the shared informer code out of `controller/`, and into `pkg` to simplify the dependency tree.
-	kubeAPI := controllerK8s.NewAPI(hc.kubeAPI, nil, nil, controllerK8s.Endpoint, controllerK8s.Pod, controllerK8s.Svc)
+	kubeAPI := controllerK8s.NewAPI(hc.kubeAPI, nil, nil, nil, controllerK8s.Endpoint, controllerK8s.Pod, controllerK8s.Svc)
 	kubeAPI.Sync(ctx.Done())
 
 	services, err := kubeAPI.Svc().Lister().Services(hc.DataPlaneNamespace).List(labels.Everything())
@@ -2231,29 +2233,21 @@ func (hc *HealthChecker) checkMisconfiguredOpaquePortAnnotations(ctx context.Con
 			continue
 		}
 
-		endpoint, err := kubeAPI.Endpoint().Lister().Endpoints(service.Namespace).Get(service.Name)
+		endpoints, err := kubeAPI.Endpoint().Lister().Endpoints(service.Namespace).Get(service.Name)
 		if err != nil {
 			return err
 		}
 
-		pods := make([]*corev1.Pod, 0)
-		for _, subset := range endpoint.Subsets {
-			for _, addr := range subset.Addresses {
-				if addr.TargetRef != nil && addr.TargetRef.Kind == "Pod" {
-					pod, err := kubeAPI.Pod().Lister().Pods(service.Namespace).Get(addr.TargetRef.Name)
-					if err != nil {
-						return err
-					}
-					pods = append(pods, pod)
-				}
-			}
+		pods, err := getEndpointsPods(endpoints, kubeAPI, service.Namespace)
+		if err != nil {
+			return err
 		}
 
-		if mismatch := misconfiguredOpaquePortAnnotationsInService(service, pods); mismatch != nil {
-			errStrings = append(
-				errStrings,
-				fmt.Sprintf("\t* %s", mismatch.Error()),
-			)
+		for pod := range pods {
+			err := misconfiguredOpaqueAnnotation(service, pod)
+			if err != nil {
+				errStrings = append(errStrings, fmt.Sprintf("\t* %s", err.Error()))
+			}
 		}
 	}
 
@@ -2264,34 +2258,154 @@ func (hc *HealthChecker) checkMisconfiguredOpaquePortAnnotations(ctx context.Con
 	return nil
 }
 
-func misconfiguredOpaquePortAnnotationsInService(service *corev1.Service, pods []*corev1.Pod) error {
-	for _, pod := range pods {
-		if err := misconfiguredOpaqueAnnotation(service, pod); err != nil {
-			return err
+// getEndpointsPods takes a collection of endpoints and returns the set of all
+// the pods that they target.
+func getEndpointsPods(endpoints *corev1.Endpoints, kubeAPI *controllerK8s.API, namespace string) (map[*corev1.Pod]struct{}, error) {
+	pods := make(map[*corev1.Pod]struct{})
+	for _, subset := range endpoints.Subsets {
+		for _, addr := range subset.Addresses {
+			if addr.TargetRef != nil && addr.TargetRef.Kind == "Pod" {
+				pod, err := kubeAPI.Pod().Lister().Pods(namespace).Get(addr.TargetRef.Name)
+				if err != nil {
+					return nil, err
+				}
+				if _, ok := pods[pod]; !ok {
+					pods[pod] = struct{}{}
+				}
+			}
 		}
 	}
-	return nil
+	return pods, nil
 }
 
 func misconfiguredOpaqueAnnotation(service *corev1.Service, pod *corev1.Pod) error {
-	svcAnnotation, svcAnnotationOk := service.Annotations[k8s.ProxyOpaquePortsAnnotation]
-	podAnnotation, podAnnotationOk := pod.Annotations[k8s.ProxyOpaquePortsAnnotation]
+	var svcPorts, podPorts []string
+	if v, ok := service.Annotations[k8s.ProxyOpaquePortsAnnotation]; ok {
+		svcPorts = strings.Split(v, ",")
+	}
+	if v, ok := pod.Annotations[k8s.ProxyOpaquePortsAnnotation]; ok {
+		podPorts = strings.Split(v, ",")
+	}
 
-	if svcAnnotationOk && podAnnotationOk {
-		if svcAnnotation != podAnnotation {
-			return fmt.Errorf("pod/%s and service/%s have the annotation %s but values don't match", pod.Name, service.Name, k8s.ProxyOpaquePortsAnnotation)
+	// First loop through the services opaque ports and assert that if the pod
+	// exposes a port that is targeted by one of these ports, then it is
+	// marked as opaque on the pod.
+	for _, p := range svcPorts {
+		port, err := strconv.Atoi(p)
+		if err != nil {
+			return fmt.Errorf("failed to convert %s to port number for pod %s", p, pod.Name)
+		}
+		err = checkPodPorts(service, pod, podPorts, port)
+		if err != nil {
+			return err
 		}
-
-		return nil
 	}
 
-	if svcAnnotationOk {
-		return fmt.Errorf("service/%s has the annotation %s but pod/%s doesn't", service.Name, k8s.ProxyOpaquePortsAnnotation, pod.Name)
+	// Next loop through the pod's opaque ports and assert that if one of
+	// the ports is targeted by a service port, then it is marked as opaque
+	// on the service.
+	for _, p := range podPorts {
+		if util.ContainsString(p, svcPorts) {
+			// The service exposes p and is marked as opaque.
+			continue
+		}
+		port, err := strconv.Atoi(p)
+		if err != nil {
+			return fmt.Errorf("failed to convert %s to port number for pod %s", p, pod.Name)
+		}
+
+		// p is marked as opaque on the pod, but the service that selects it
+		// does not have it marked as opaque. We first check if the service
+		// exposes it as a service or integer targetPort.
+		ok, err := checkServiceIntPorts(service, svcPorts, port)
+		if err != nil {
+			return err
+		}
+		if ok {
+			// The service targets the port as an integer and is marked as
+			// opaque so continue checking other pod ports.
+			continue
+		}
+
+		// The service does not expose p as a service or integer targetPort.
+		// We now check if it targets it as a named port, and if so, that the
+		// service port is marked as opaque.
+		err = checkServiceNamePorts(service, pod, port, svcPorts)
+		if err != nil {
+			return err
+		}
 	}
-	if podAnnotationOk {
-		return fmt.Errorf("pod/%s has the annotation %s but service/%s doesn't", pod.Name, k8s.ProxyOpaquePortsAnnotation, service.Name)
+	return nil
+}
+
+func checkPodPorts(service *corev1.Service, pod *corev1.Pod, podPorts []string, port int) error {
+	for _, sp := range service.Spec.Ports {
+		if sp.Port == int32(port) {
+			for _, c := range pod.Spec.Containers {
+				for _, cp := range c.Ports {
+					if cp.ContainerPort == sp.TargetPort.IntVal || cp.Name == sp.TargetPort.StrVal {
+						// The pod exposes a container port that would be
+						// targeted by this service port
+						var strPort string
+						if sp.TargetPort.Type == 0 {
+							strPort = strconv.Itoa(int(sp.TargetPort.IntVal))
+						} else {
+							strPort = strconv.Itoa(int(cp.ContainerPort))
+						}
+						if util.ContainsString(strPort, podPorts) {
+							return nil
+						}
+						return fmt.Errorf("service %s expects %d to be opaque; add it to pod %s's %s annotation", service.Name, port, pod.Name, k8s.ProxyOpaquePortsAnnotation)
+					}
+				}
+			}
+		}
 	}
+	return nil
+}
 
+func checkServiceIntPorts(service *corev1.Service, svcPorts []string, port int) (bool, error) {
+	for _, p := range service.Spec.Ports {
+		if p.TargetPort.Type == 0 && p.TargetPort.IntVal == 0 {
+			if p.Port == int32(port) {
+				// The service does not have a target port, so its service
+				// port should be marked as opaque.
+				return false, fmt.Errorf("service %s targets the opaque port %d; add it to its %s annotation", service.Name, port, k8s.ProxyOpaquePortsAnnotation)
+			}
+		}
+		if p.TargetPort.IntVal == int32(port) {
+			svcPort := strconv.Itoa(int(p.Port))
+			if util.ContainsString(svcPort, svcPorts) {
+				// The service exposes svcPort which targets p and svcPort
+				// is properly as opaque.
+				return true, nil
+			}
+			return false, fmt.Errorf("service %s targets the opaque port %d through %d; add %d to its %s annotation", service.Name, port, p.Port, p.Port, k8s.ProxyOpaquePortsAnnotation)
+		}
+	}
+	return false, nil
+}
+
+func checkServiceNamePorts(service *corev1.Service, pod *corev1.Pod, port int, svcPorts []string) error {
+	for _, p := range service.Spec.Ports {
+		for _, c := range pod.Spec.Containers {
+			for _, cp := range c.Ports {
+				if cp.ContainerPort == int32(port) {
+					// This is the containerPort that maps to the opaque port
+					// we are currently checking.
+					if cp.Name == p.TargetPort.StrVal {
+						svcPort := strconv.Itoa(int(p.Port))
+						if util.ContainsString(svcPort, svcPorts) {
+							// The service targets the container port by name
+							// and is marked as opaque.
+							return nil
+						}
+						return fmt.Errorf("service %s targets the opaque port %s through %d; add %d to its %s annotation", service.Name, cp.Name, p.Port, p.Port, k8s.ProxyOpaquePortsAnnotation)
+					}
+				}
+			}
+		}
+	}
 	return nil
 }
 
diff --git a/pkg/k8s/k8s.go b/pkg/k8s/k8s.go
index a057409d6..ea8f3ce11 100644
--- a/pkg/k8s/k8s.go
+++ b/pkg/k8s/k8s.go
@@ -25,6 +25,8 @@ const (
 	StatefulSet           = "statefulset"
 	TrafficSplit          = "trafficsplit"
 	Node                  = "node"
+	Server                = "server"
+	ServerAuthorization   = "serverauthorization"
 
 	ServiceProfileAPIVersion = "linkerd.io/v1alpha2"
 	ServiceProfileKind       = "ServiceProfile"
@@ -57,6 +59,8 @@ var AllResources = []string{
 	ReplicaSet,
 	Service,
 	ServiceProfile,
+	Server,
+	ServerAuthorization,
 	StatefulSet,
 	TrafficSplit,
 }
@@ -104,6 +108,8 @@ var resourceNames = []resourceName{
 	{"rs", "replicaset", "replicasets"},
 	{"svc", "service", "services"},
 	{"sp", "serviceprofile", "serviceprofiles"},
+	{"saz", "serverauthorization", "serverauthorizations"},
+	{"srv", "server", "servers"},
 	{"sts", "statefulset", "statefulsets"},
 	{"ts", "trafficsplit", "trafficsplits"},
 	{"ln", "link", "links"},
diff --git a/policy-controller/Cargo.toml b/policy-controller/Cargo.toml
index 2c38168d0..16fb73f8a 100644
--- a/policy-controller/Cargo.toml
+++ b/policy-controller/Cargo.toml
@@ -22,7 +22,7 @@ linkerd-policy-controller-k8s-index = { path = "./k8s/index" }
 linkerd-policy-controller-k8s-api = { path = "./k8s/api" }
 serde_json = "1"
 structopt = "0.3"
-tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros", "parking_lot", "signal", "sync"] }
+tokio = { version = "1", features = ["macros", "parking_lot", "rt", "rt-multi-thread", "signal", "sync"] }
 tracing = "0.1"
-tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "fmt", "tracing-log"] }
+tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "fmt", "json", "smallvec", "tracing-log"] }
 warp = { version = "0.3", default-features = false, features = ["tls"] }
diff --git a/policy-controller/src/main.rs b/policy-controller/src/main.rs
index a0e9615bc..550405473 100644
--- a/policy-controller/src/main.rs
+++ b/policy-controller/src/main.rs
@@ -1,7 +1,7 @@
 #![deny(warnings, rust_2018_idioms)]
 #![forbid(unsafe_code)]
 
-use anyhow::{Context, Result};
+use anyhow::{bail, Context, Error, Result};
 use futures::{future, prelude::*};
 use linkerd_policy_controller::k8s::DefaultPolicy;
 use linkerd_policy_controller::{admin, admission};
@@ -10,10 +10,22 @@ use std::net::SocketAddr;
 use structopt::StructOpt;
 use tokio::{sync::watch, time};
 use tracing::{debug, info, info_span, instrument, Instrument};
+use tracing_subscriber::{fmt::format, prelude::*, EnvFilter};
 
 #[derive(Debug, StructOpt)]
 #[structopt(name = "policy", about = "A policy resource prototype")]
 struct Args {
+    #[structopt(
+        parse(try_from_str),
+        long,
+        default_value = "linkerd=info,warn",
+        env = "LINKERD_POLICY_CONTROLLER_LOG"
+    )]
+    log_level: EnvFilter,
+
+    #[structopt(long, default_value = "plain")]
+    log_format: LogFormat,
+
     #[structopt(long, default_value = "0.0.0.0:8080")]
     admin_addr: SocketAddr,
 
@@ -39,10 +51,14 @@ struct Args {
     default_policy: DefaultPolicy,
 }
 
+#[derive(Clone, Debug)]
+enum LogFormat {
+    Json,
+    Plain,
+}
+
 #[tokio::main]
 async fn main() -> Result<()> {
-    tracing_subscriber::fmt::init();
-
     let Args {
         admin_addr,
         grpc_addr,
@@ -50,8 +66,12 @@ async fn main() -> Result<()> {
         identity_domain,
         cluster_networks: IpNets(cluster_networks),
         default_policy,
+        log_level,
+        log_format,
     } = Args::from_args();
 
+    log_init(log_level, log_format)?;
+
     let (drain_tx, drain_rx) = drain::channel();
 
     // Load a Kubernetes client from the environment (check for in-cluster configuration first).
@@ -159,3 +179,47 @@ async fn sigterm() {
         _ => future::pending().await,
     };
 }
+
+fn log_init(filter: EnvFilter, format: LogFormat) -> Result<()> {
+    let registry = tracing_subscriber::registry().with(filter);
+
+    match format {
+        LogFormat::Plain => registry.with(tracing_subscriber::fmt::layer()).try_init()?,
+
+        LogFormat::Json => {
+            let event_fmt = tracing_subscriber::fmt::format()
+                // Configure the formatter to output JSON logs.
+                .json()
+                // Output the current span context as a JSON list.
+                .with_span_list(true)
+                // Don't output a field for the current span, since this
+                // would duplicate information already in the span list.
+                .with_current_span(false);
+
+            // Use the JSON event formatter and the JSON field formatter.
+            let fmt = tracing_subscriber::fmt::layer()
+                .event_format(event_fmt)
+                .fmt_fields(format::JsonFields::default());
+
+            registry.with(fmt).try_init()?
+        }
+    };
+
+    Ok(())
+}
+
+// === impl LogFormat ===
+
+impl std::str::FromStr for LogFormat {
+    type Err = Error;
+
+    fn from_str(s: &str) -> Result<Self> {
+        if s == "json" {
+            Ok(Self::Json)
+        } else if s == "plain" {
+            Ok(Self::Plain)
+        } else {
+            bail!("invalid log format: {}", s)
+        }
+    }
+}
diff --git a/viz/charts/linkerd-viz/templates/metrics-api-rbac.yaml b/viz/charts/linkerd-viz/templates/metrics-api-rbac.yaml
index e68c5d90a..3b230fbf9 100644
--- a/viz/charts/linkerd-viz/templates/metrics-api-rbac.yaml
+++ b/viz/charts/linkerd-viz/templates/metrics-api-rbac.yaml
@@ -25,6 +25,9 @@ rules:
 - apiGroups: ["split.smi-spec.io"]
   resources: ["trafficsplits"]
   verbs: ["list", "get", "watch"]
+- apiGroups: ["policy.linkerd.io"]
+  resources: ["servers", "serverauthorizations"]
+  verbs: ["list", "get"]
 ---
 kind: ClusterRoleBinding
 apiVersion: rbac.authorization.k8s.io/v1
diff --git a/viz/cmd/stat.go b/viz/cmd/stat.go
index a61d9cc99..ac8d4b830 100644
--- a/viz/cmd/stat.go
+++ b/viz/cmd/stat.go
@@ -121,7 +121,9 @@ func NewCmdStat() *cobra.Command {
   * statefulsets
   * trafficsplits
   * authorities (not supported in --from)
-  * services (only supported if a --from is also specified, or as a --to)
+  * services (not supported in --from)
+  * servers (not supported in --from)
+  * serverauthorizations (not supported in --from)
   * all (all resource types, not supported in --from or --to)
 
 This command will hide resources that have completed, such as pods that are in the Succeeded or Failed phases.
@@ -178,7 +180,14 @@ If no resource name is specified, displays stats about all resources of the spec
   linkerd viz stat namespaces --from ns/default
 
   # Get all inbound stats to the test namespace.
-  linkerd viz stat ns/test`,
+  linkerd viz stat ns/test
+
+  # Get all inbound stats to the emoji-grpc server
+  linkerd viz stat server/emoji-grpc
+
+  # Get all inbound stats to the web-public server authorization resource
+  linkerd viz stat serverauthorization/web-public
+  `,
 		Args: cobra.MinimumNArgs(1),
 		ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
 
@@ -317,12 +326,17 @@ type rowStats struct {
 	tcpWriteBytes      float64
 }
 
+type srvStats struct {
+	unauthorizedRate float64
+}
+
 type row struct {
 	meshed string
 	status string
 	*rowStats
 	*tsStats
 	*dstStats
+	*srvStats
 }
 
 type tsStats struct {
@@ -349,7 +363,7 @@ func statHasRequestData(stat *pb.BasicStats) bool {
 }
 
 func isPodOwnerResource(typ string) bool {
-	return typ != k8s.TrafficSplit && typ != k8s.Authority && typ != k8s.Service
+	return typ != k8s.TrafficSplit && typ != k8s.Authority && typ != k8s.Service && typ != k8s.Server && typ != k8s.ServerAuthorization
 }
 
 func writeStatsToBuffer(rows []*pb.StatTable_PodGroup_Row, w *tabwriter.Writer, options *statOptions) {
@@ -395,6 +409,7 @@ func writeStatsToBuffer(rows []*pb.StatTable_PodGroup_Row, w *tabwriter.Writer,
 		if r.Resource.Type == k8s.TrafficSplit || (r.Resource.Type == k8s.Service && r.TsStats != nil) {
 			key = fmt.Sprintf("%s/%s/%s", namespace, name, r.TsStats.Leaf)
 		}
+
 		resourceKey := r.Resource.Type
 
 		if _, ok := statTables[resourceKey]; !ok {
@@ -409,13 +424,16 @@ func writeStatsToBuffer(rows []*pb.StatTable_PodGroup_Row, w *tabwriter.Writer,
 			maxNamespaceLength = len(namespace)
 		}
 
-		meshedCount := fmt.Sprintf("%d/%d", r.MeshedPodCount, r.RunningPodCount)
-		if resourceKey == k8s.Authority || resourceKey == k8s.Service {
-			meshedCount = "-"
-		}
-		statTables[resourceKey][key] = &row{
-			meshed: meshedCount,
-			status: r.Status,
+		statTables[resourceKey][key] = &row{}
+		if resourceKey != k8s.Server && resourceKey != k8s.ServerAuthorization {
+			meshedCount := fmt.Sprintf("%d/%d", r.MeshedPodCount, r.RunningPodCount)
+			if resourceKey == k8s.Authority || resourceKey == k8s.Service {
+				meshedCount = "-"
+			}
+			statTables[resourceKey][key] = &row{
+				meshed: meshedCount,
+				status: r.Status,
+			}
 		}
 
 		if r.Stats != nil && statHasRequestData(r.Stats) {
@@ -467,6 +485,12 @@ func writeStatsToBuffer(rows []*pb.StatTable_PodGroup_Row, w *tabwriter.Writer,
 				}
 			}
 		}
+
+		if r.SrvStats != nil {
+			statTables[resourceKey][key].srvStats = &srvStats{
+				unauthorizedRate: getSuccessRate(r.SrvStats.GetDeniedCount(), r.SrvStats.GetAllowedCount()),
+			}
+		}
 	}
 
 	switch options.outputFormat {
@@ -509,7 +533,7 @@ func showTCPBytes(options *statOptions, resourceType string) bool {
 }
 
 func showTCPConns(resourceType string) bool {
-	return resourceType != k8s.Authority && resourceType != k8s.TrafficSplit
+	return resourceType != k8s.Authority && resourceType != k8s.TrafficSplit && resourceType != k8s.ServerAuthorization
 }
 
 func printSingleStatTable(stats map[string]*row, resourceTypeLabel, resourceType string, w *tabwriter.Writer, maxNameLength, maxNamespaceLength, maxLeafLength, maxApexLength, maxDstLength, maxWeightLength int, options *statOptions) {
@@ -556,10 +580,14 @@ func printSingleStatTable(stats map[string]*row, resourceTypeLabel, resourceType
 			fmt.Sprintf(apexTemplate, apexHeader),
 			fmt.Sprintf(leafTemplate, leafHeader),
 			fmt.Sprintf(weightTemplate, weightHeader))
-	} else {
+	} else if resourceType != k8s.Server && resourceType != k8s.ServerAuthorization {
 		headers = append(headers, "MESHED")
 	}
 
+	if resourceType == k8s.Server {
+		headers = append(headers, "UNAUTHORIZED")
+	}
+
 	headers = append(headers, []string{
 		"SUCCESS",
 		"RPS",
@@ -568,7 +596,7 @@ func printSingleStatTable(stats map[string]*row, resourceTypeLabel, resourceType
 		"LATENCY_P99",
 	}...)
 
-	if resourceType != k8s.TrafficSplit {
+	if showTCPConns(resourceType) {
 		headers = append(headers, "TCP_CONN")
 	}
 
@@ -588,7 +616,7 @@ func printSingleStatTable(stats map[string]*row, resourceTypeLabel, resourceType
 		namespace, name := namespaceName(resourceTypeLabel, key)
 		values := make([]interface{}, 0)
 		templateString := "%s\t%s\t%.2f%%\t%.1frps\t%dms\t%dms\t%dms\t"
-		templateStringEmpty := "%s\t%s\t-\t-\t-\t-\t-\t-\t"
+		templateStringEmpty := "%s\t%s\t-\t-\t-\t-\t-\t"
 		if resourceType == k8s.Pod {
 			templateString = "%s\t" + templateString
 			templateStringEmpty = "%s\t" + templateStringEmpty
@@ -600,15 +628,17 @@ func printSingleStatTable(stats map[string]*row, resourceTypeLabel, resourceType
 		} else if hasDstStats {
 			templateString = "%s\t%s\t%s\t%.2f%%\t%.1frps\t%dms\t%dms\t%dms\t"
 			templateStringEmpty = "%s\t%s\t%s\t-\t-\t-\t-\t-\t"
+		} else if resourceType == k8s.ServerAuthorization {
+			templateString = "%s\t%.2f%%\t%.1frps\t%dms\t%dms\t%dms\t"
+			templateStringEmpty = "%s\t-\t-\t-\t-\t-\t"
+		} else if resourceType == k8s.Server {
+			templateString = "%s\t%.1frps\t%.2f%%\t%.1frps\t%dms\t%dms\t%dms\t"
+			templateStringEmpty = "%s\t%.1frps\t-\t-\t-\t-\t-\t"
 		}
 
-		if !showTCPConns(resourceType) {
-			if resourceType == k8s.Authority {
-				// always show TCP Connections as - for Authorities
-				templateString = templateString + "-\t"
-			}
-		} else {
+		if showTCPConns(resourceType) {
 			templateString = templateString + "%d\t"
+			templateStringEmpty = templateStringEmpty + "-\t"
 		}
 
 		if showTCPBytes(options, resourceType) {
@@ -664,12 +694,22 @@ func printSingleStatTable(stats map[string]*row, resourceTypeLabel, resourceType
 				stats[key].dstStats.dst+strings.Repeat(" ", dstPadding),
 				stats[key].dstStats.weight,
 			)
-		} else {
+		} else if resourceType != k8s.ServerAuthorization && resourceType != k8s.Server {
 			values = append(values, []interface{}{
 				stats[key].meshed,
 			}...)
 		}
 
+		if resourceType == k8s.Server {
+			var unauthorizedRate float64
+			if stats[key].srvStats != nil {
+				unauthorizedRate = stats[key].srvStats.unauthorizedRate
+			}
+			values = append(values, []interface{}{
+				unauthorizedRate,
+			}...)
+		}
+
 		if stats[key].rowStats != nil {
 			values = append(values, []interface{}{
 				stats[key].successRate * 100,
diff --git a/viz/metrics-api/gen/viz/viz.pb.go b/viz/metrics-api/gen/viz/viz.pb.go
index 993516399..de286b44c 100644
--- a/viz/metrics-api/gen/viz/viz.pb.go
+++ b/viz/metrics-api/gen/viz/viz.pb.go
@@ -1854,6 +1854,61 @@ func (x *TrafficSplitStats) GetWeight() string {
 	return ""
 }
 
+type ServerStats struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	AllowedCount uint64 `protobuf:"varint,1,opt,name=allowed_count,json=allowedCount,proto3" json:"allowed_count,omitempty"`
+	DeniedCount  uint64 `protobuf:"varint,2,opt,name=denied_count,json=deniedCount,proto3" json:"denied_count,omitempty"`
+}
+
+func (x *ServerStats) Reset() {
+	*x = ServerStats{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_viz_proto_msgTypes[24]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ServerStats) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ServerStats) ProtoMessage() {}
+
+func (x *ServerStats) ProtoReflect() protoreflect.Message {
+	mi := &file_viz_proto_msgTypes[24]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ServerStats.ProtoReflect.Descriptor instead.
+func (*ServerStats) Descriptor() ([]byte, []int) {
+	return file_viz_proto_rawDescGZIP(), []int{24}
+}
+
+func (x *ServerStats) GetAllowedCount() uint64 {
+	if x != nil {
+		return x.AllowedCount
+	}
+	return 0
+}
+
+func (x *ServerStats) GetDeniedCount() uint64 {
+	if x != nil {
+		return x.DeniedCount
+	}
+	return 0
+}
+
 type StatTable struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -1867,7 +1922,7 @@ type StatTable struct {
 func (x *StatTable) Reset() {
 	*x = StatTable{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_viz_proto_msgTypes[24]
+		mi := &file_viz_proto_msgTypes[25]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1880,7 +1935,7 @@ func (x *StatTable) String() string {
 func (*StatTable) ProtoMessage() {}
 
 func (x *StatTable) ProtoReflect() protoreflect.Message {
-	mi := &file_viz_proto_msgTypes[24]
+	mi := &file_viz_proto_msgTypes[25]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1893,7 +1948,7 @@ func (x *StatTable) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use StatTable.ProtoReflect.Descriptor instead.
 func (*StatTable) Descriptor() ([]byte, []int) {
-	return file_viz_proto_rawDescGZIP(), []int{24}
+	return file_viz_proto_rawDescGZIP(), []int{25}
 }
 
 func (m *StatTable) GetTable() isStatTable_Table {
@@ -1931,7 +1986,7 @@ type EdgesRequest struct {
 func (x *EdgesRequest) Reset() {
 	*x = EdgesRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_viz_proto_msgTypes[25]
+		mi := &file_viz_proto_msgTypes[26]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1944,7 +1999,7 @@ func (x *EdgesRequest) String() string {
 func (*EdgesRequest) ProtoMessage() {}
 
 func (x *EdgesRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_viz_proto_msgTypes[25]
+	mi := &file_viz_proto_msgTypes[26]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1957,7 +2012,7 @@ func (x *EdgesRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use EdgesRequest.ProtoReflect.Descriptor instead.
 func (*EdgesRequest) Descriptor() ([]byte, []int) {
-	return file_viz_proto_rawDescGZIP(), []int{25}
+	return file_viz_proto_rawDescGZIP(), []int{26}
 }
 
 func (x *EdgesRequest) GetSelector() *ResourceSelection {
@@ -1981,7 +2036,7 @@ type EdgesResponse struct {
 func (x *EdgesResponse) Reset() {
 	*x = EdgesResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_viz_proto_msgTypes[26]
+		mi := &file_viz_proto_msgTypes[27]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1994,7 +2049,7 @@ func (x *EdgesResponse) String() string {
 func (*EdgesResponse) ProtoMessage() {}
 
 func (x *EdgesResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_viz_proto_msgTypes[26]
+	mi := &file_viz_proto_msgTypes[27]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2007,7 +2062,7 @@ func (x *EdgesResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use EdgesResponse.ProtoReflect.Descriptor instead.
 func (*EdgesResponse) Descriptor() ([]byte, []int) {
-	return file_viz_proto_rawDescGZIP(), []int{26}
+	return file_viz_proto_rawDescGZIP(), []int{27}
 }
 
 func (m *EdgesResponse) GetResponse() isEdgesResponse_Response {
@@ -2062,7 +2117,7 @@ type Edge struct {
 func (x *Edge) Reset() {
 	*x = Edge{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_viz_proto_msgTypes[27]
+		mi := &file_viz_proto_msgTypes[28]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2075,7 +2130,7 @@ func (x *Edge) String() string {
 func (*Edge) ProtoMessage() {}
 
 func (x *Edge) ProtoReflect() protoreflect.Message {
-	mi := &file_viz_proto_msgTypes[27]
+	mi := &file_viz_proto_msgTypes[28]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2088,7 +2143,7 @@ func (x *Edge) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use Edge.ProtoReflect.Descriptor instead.
 func (*Edge) Descriptor() ([]byte, []int) {
-	return file_viz_proto_rawDescGZIP(), []int{27}
+	return file_viz_proto_rawDescGZIP(), []int{28}
 }
 
 func (x *Edge) GetSrc() *Resource {
@@ -2142,7 +2197,7 @@ type TopRoutesRequest struct {
 func (x *TopRoutesRequest) Reset() {
 	*x = TopRoutesRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_viz_proto_msgTypes[28]
+		mi := &file_viz_proto_msgTypes[29]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2155,7 +2210,7 @@ func (x *TopRoutesRequest) String() string {
 func (*TopRoutesRequest) ProtoMessage() {}
 
 func (x *TopRoutesRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_viz_proto_msgTypes[28]
+	mi := &file_viz_proto_msgTypes[29]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2168,7 +2223,7 @@ func (x *TopRoutesRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use TopRoutesRequest.ProtoReflect.Descriptor instead.
 func (*TopRoutesRequest) Descriptor() ([]byte, []int) {
-	return file_viz_proto_rawDescGZIP(), []int{28}
+	return file_viz_proto_rawDescGZIP(), []int{29}
 }
 
 func (x *TopRoutesRequest) GetSelector() *ResourceSelection {
@@ -2236,7 +2291,7 @@ type TopRoutesResponse struct {
 func (x *TopRoutesResponse) Reset() {
 	*x = TopRoutesResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_viz_proto_msgTypes[29]
+		mi := &file_viz_proto_msgTypes[30]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2249,7 +2304,7 @@ func (x *TopRoutesResponse) String() string {
 func (*TopRoutesResponse) ProtoMessage() {}
 
 func (x *TopRoutesResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_viz_proto_msgTypes[29]
+	mi := &file_viz_proto_msgTypes[30]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2262,7 +2317,7 @@ func (x *TopRoutesResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use TopRoutesResponse.ProtoReflect.Descriptor instead.
 func (*TopRoutesResponse) Descriptor() ([]byte, []int) {
-	return file_viz_proto_rawDescGZIP(), []int{29}
+	return file_viz_proto_rawDescGZIP(), []int{30}
 }
 
 func (m *TopRoutesResponse) GetResponse() isTopRoutesResponse_Response {
@@ -2314,7 +2369,7 @@ type RouteTable struct {
 func (x *RouteTable) Reset() {
 	*x = RouteTable{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_viz_proto_msgTypes[30]
+		mi := &file_viz_proto_msgTypes[31]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2327,7 +2382,7 @@ func (x *RouteTable) String() string {
 func (*RouteTable) ProtoMessage() {}
 
 func (x *RouteTable) ProtoReflect() protoreflect.Message {
-	mi := &file_viz_proto_msgTypes[30]
+	mi := &file_viz_proto_msgTypes[31]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2340,7 +2395,7 @@ func (x *RouteTable) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use RouteTable.ProtoReflect.Descriptor instead.
 func (*RouteTable) Descriptor() ([]byte, []int) {
-	return file_viz_proto_rawDescGZIP(), []int{30}
+	return file_viz_proto_rawDescGZIP(), []int{31}
 }
 
 func (x *RouteTable) GetRows() []*RouteTable_Row {
@@ -2368,7 +2423,7 @@ type GatewaysTable struct {
 func (x *GatewaysTable) Reset() {
 	*x = GatewaysTable{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_viz_proto_msgTypes[31]
+		mi := &file_viz_proto_msgTypes[32]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2381,7 +2436,7 @@ func (x *GatewaysTable) String() string {
 func (*GatewaysTable) ProtoMessage() {}
 
 func (x *GatewaysTable) ProtoReflect() protoreflect.Message {
-	mi := &file_viz_proto_msgTypes[31]
+	mi := &file_viz_proto_msgTypes[32]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2394,7 +2449,7 @@ func (x *GatewaysTable) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use GatewaysTable.ProtoReflect.Descriptor instead.
 func (*GatewaysTable) Descriptor() ([]byte, []int) {
-	return file_viz_proto_rawDescGZIP(), []int{31}
+	return file_viz_proto_rawDescGZIP(), []int{32}
 }
 
 func (x *GatewaysTable) GetRows() []*GatewaysTable_Row {
@@ -2417,7 +2472,7 @@ type GatewaysRequest struct {
 func (x *GatewaysRequest) Reset() {
 	*x = GatewaysRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_viz_proto_msgTypes[32]
+		mi := &file_viz_proto_msgTypes[33]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2430,7 +2485,7 @@ func (x *GatewaysRequest) String() string {
 func (*GatewaysRequest) ProtoMessage() {}
 
 func (x *GatewaysRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_viz_proto_msgTypes[32]
+	mi := &file_viz_proto_msgTypes[33]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2443,7 +2498,7 @@ func (x *GatewaysRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use GatewaysRequest.ProtoReflect.Descriptor instead.
 func (*GatewaysRequest) Descriptor() ([]byte, []int) {
-	return file_viz_proto_rawDescGZIP(), []int{32}
+	return file_viz_proto_rawDescGZIP(), []int{33}
 }
 
 func (x *GatewaysRequest) GetRemoteClusterName() string {
@@ -2481,7 +2536,7 @@ type GatewaysResponse struct {
 func (x *GatewaysResponse) Reset() {
 	*x = GatewaysResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_viz_proto_msgTypes[33]
+		mi := &file_viz_proto_msgTypes[34]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2494,7 +2549,7 @@ func (x *GatewaysResponse) String() string {
 func (*GatewaysResponse) ProtoMessage() {}
 
 func (x *GatewaysResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_viz_proto_msgTypes[33]
+	mi := &file_viz_proto_msgTypes[34]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2507,7 +2562,7 @@ func (x *GatewaysResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use GatewaysResponse.ProtoReflect.Descriptor instead.
 func (*GatewaysResponse) Descriptor() ([]byte, []int) {
-	return file_viz_proto_rawDescGZIP(), []int{33}
+	return file_viz_proto_rawDescGZIP(), []int{34}
 }
 
 func (m *GatewaysResponse) GetResponse() isGatewaysResponse_Response {
@@ -2567,7 +2622,7 @@ type Headers_Header struct {
 func (x *Headers_Header) Reset() {
 	*x = Headers_Header{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_viz_proto_msgTypes[34]
+		mi := &file_viz_proto_msgTypes[35]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2580,7 +2635,7 @@ func (x *Headers_Header) String() string {
 func (*Headers_Header) ProtoMessage() {}
 
 func (x *Headers_Header) ProtoReflect() protoreflect.Message {
-	mi := &file_viz_proto_msgTypes[34]
+	mi := &file_viz_proto_msgTypes[35]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2653,7 +2708,7 @@ type PodErrors_PodError struct {
 func (x *PodErrors_PodError) Reset() {
 	*x = PodErrors_PodError{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_viz_proto_msgTypes[35]
+		mi := &file_viz_proto_msgTypes[36]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2666,7 +2721,7 @@ func (x *PodErrors_PodError) String() string {
 func (*PodErrors_PodError) ProtoMessage() {}
 
 func (x *PodErrors_PodError) ProtoReflect() protoreflect.Message {
-	mi := &file_viz_proto_msgTypes[35]
+	mi := &file_viz_proto_msgTypes[36]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2721,7 +2776,7 @@ type PodErrors_PodError_ContainerError struct {
 func (x *PodErrors_PodError_ContainerError) Reset() {
 	*x = PodErrors_PodError_ContainerError{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_viz_proto_msgTypes[36]
+		mi := &file_viz_proto_msgTypes[37]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2734,7 +2789,7 @@ func (x *PodErrors_PodError_ContainerError) String() string {
 func (*PodErrors_PodError_ContainerError) ProtoMessage() {}
 
 func (x *PodErrors_PodError_ContainerError) ProtoReflect() protoreflect.Message {
-	mi := &file_viz_proto_msgTypes[36]
+	mi := &file_viz_proto_msgTypes[37]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2789,7 +2844,7 @@ type StatSummaryResponse_Ok struct {
 func (x *StatSummaryResponse_Ok) Reset() {
 	*x = StatSummaryResponse_Ok{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_viz_proto_msgTypes[37]
+		mi := &file_viz_proto_msgTypes[38]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2802,7 +2857,7 @@ func (x *StatSummaryResponse_Ok) String() string {
 func (*StatSummaryResponse_Ok) ProtoMessage() {}
 
 func (x *StatSummaryResponse_Ok) ProtoReflect() protoreflect.Message {
-	mi := &file_viz_proto_msgTypes[37]
+	mi := &file_viz_proto_msgTypes[38]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2836,7 +2891,7 @@ type StatTable_PodGroup struct {
 func (x *StatTable_PodGroup) Reset() {
 	*x = StatTable_PodGroup{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_viz_proto_msgTypes[38]
+		mi := &file_viz_proto_msgTypes[39]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2849,7 +2904,7 @@ func (x *StatTable_PodGroup) String() string {
 func (*StatTable_PodGroup) ProtoMessage() {}
 
 func (x *StatTable_PodGroup) ProtoReflect() protoreflect.Message {
-	mi := &file_viz_proto_msgTypes[38]
+	mi := &file_viz_proto_msgTypes[39]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2862,7 +2917,7 @@ func (x *StatTable_PodGroup) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use StatTable_PodGroup.ProtoReflect.Descriptor instead.
 func (*StatTable_PodGroup) Descriptor() ([]byte, []int) {
-	return file_viz_proto_rawDescGZIP(), []int{24, 0}
+	return file_viz_proto_rawDescGZIP(), []int{25, 0}
 }
 
 func (x *StatTable_PodGroup) GetRows() []*StatTable_PodGroup_Row {
@@ -2890,6 +2945,7 @@ type StatTable_PodGroup_Row struct {
 	Stats          *BasicStats        `protobuf:"bytes,5,opt,name=stats,proto3" json:"stats,omitempty"`
 	TcpStats       *TcpStats          `protobuf:"bytes,8,opt,name=tcp_stats,json=tcpStats,proto3" json:"tcp_stats,omitempty"`
 	TsStats        *TrafficSplitStats `protobuf:"bytes,10,opt,name=ts_stats,json=tsStats,proto3" json:"ts_stats,omitempty"`
+	SrvStats       *ServerStats       `protobuf:"bytes,11,opt,name=srv_stats,json=srvStats,proto3" json:"srv_stats,omitempty"`
 	// Stores a set of errors for each pod name. If a pod has no errors, it may be omitted.
 	ErrorsByPod map[string]*PodErrors `protobuf:"bytes,7,rep,name=errors_by_pod,json=errorsByPod,proto3" json:"errors_by_pod,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
 }
@@ -2897,7 +2953,7 @@ type StatTable_PodGroup_Row struct {
 func (x *StatTable_PodGroup_Row) Reset() {
 	*x = StatTable_PodGroup_Row{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_viz_proto_msgTypes[39]
+		mi := &file_viz_proto_msgTypes[40]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2910,7 +2966,7 @@ func (x *StatTable_PodGroup_Row) String() string {
 func (*StatTable_PodGroup_Row) ProtoMessage() {}
 
 func (x *StatTable_PodGroup_Row) ProtoReflect() protoreflect.Message {
-	mi := &file_viz_proto_msgTypes[39]
+	mi := &file_viz_proto_msgTypes[40]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2923,7 +2979,7 @@ func (x *StatTable_PodGroup_Row) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use StatTable_PodGroup_Row.ProtoReflect.Descriptor instead.
 func (*StatTable_PodGroup_Row) Descriptor() ([]byte, []int) {
-	return file_viz_proto_rawDescGZIP(), []int{24, 0, 0}
+	return file_viz_proto_rawDescGZIP(), []int{25, 0, 0}
 }
 
 func (x *StatTable_PodGroup_Row) GetResource() *Resource {
@@ -2989,6 +3045,13 @@ func (x *StatTable_PodGroup_Row) GetTsStats() *TrafficSplitStats {
 	return nil
 }
 
+func (x *StatTable_PodGroup_Row) GetSrvStats() *ServerStats {
+	if x != nil {
+		return x.SrvStats
+	}
+	return nil
+}
+
 func (x *StatTable_PodGroup_Row) GetErrorsByPod() map[string]*PodErrors {
 	if x != nil {
 		return x.ErrorsByPod
@@ -3007,7 +3070,7 @@ type EdgesResponse_Ok struct {
 func (x *EdgesResponse_Ok) Reset() {
 	*x = EdgesResponse_Ok{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_viz_proto_msgTypes[41]
+		mi := &file_viz_proto_msgTypes[42]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -3020,7 +3083,7 @@ func (x *EdgesResponse_Ok) String() string {
 func (*EdgesResponse_Ok) ProtoMessage() {}
 
 func (x *EdgesResponse_Ok) ProtoReflect() protoreflect.Message {
-	mi := &file_viz_proto_msgTypes[41]
+	mi := &file_viz_proto_msgTypes[42]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -3033,7 +3096,7 @@ func (x *EdgesResponse_Ok) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use EdgesResponse_Ok.ProtoReflect.Descriptor instead.
 func (*EdgesResponse_Ok) Descriptor() ([]byte, []int) {
-	return file_viz_proto_rawDescGZIP(), []int{26, 0}
+	return file_viz_proto_rawDescGZIP(), []int{27, 0}
 }
 
 func (x *EdgesResponse_Ok) GetEdges() []*Edge {
@@ -3054,7 +3117,7 @@ type TopRoutesResponse_Ok struct {
 func (x *TopRoutesResponse_Ok) Reset() {
 	*x = TopRoutesResponse_Ok{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_viz_proto_msgTypes[42]
+		mi := &file_viz_proto_msgTypes[43]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -3067,7 +3130,7 @@ func (x *TopRoutesResponse_Ok) String() string {
 func (*TopRoutesResponse_Ok) ProtoMessage() {}
 
 func (x *TopRoutesResponse_Ok) ProtoReflect() protoreflect.Message {
-	mi := &file_viz_proto_msgTypes[42]
+	mi := &file_viz_proto_msgTypes[43]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -3080,7 +3143,7 @@ func (x *TopRoutesResponse_Ok) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use TopRoutesResponse_Ok.ProtoReflect.Descriptor instead.
 func (*TopRoutesResponse_Ok) Descriptor() ([]byte, []int) {
-	return file_viz_proto_rawDescGZIP(), []int{29, 0}
+	return file_viz_proto_rawDescGZIP(), []int{30, 0}
 }
 
 func (x *TopRoutesResponse_Ok) GetRoutes() []*RouteTable {
@@ -3104,7 +3167,7 @@ type RouteTable_Row struct {
 func (x *RouteTable_Row) Reset() {
 	*x = RouteTable_Row{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_viz_proto_msgTypes[43]
+		mi := &file_viz_proto_msgTypes[44]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -3117,7 +3180,7 @@ func (x *RouteTable_Row) String() string {
 func (*RouteTable_Row) ProtoMessage() {}
 
 func (x *RouteTable_Row) ProtoReflect() protoreflect.Message {
-	mi := &file_viz_proto_msgTypes[43]
+	mi := &file_viz_proto_msgTypes[44]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -3130,7 +3193,7 @@ func (x *RouteTable_Row) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use RouteTable_Row.ProtoReflect.Descriptor instead.
 func (*RouteTable_Row) Descriptor() ([]byte, []int) {
-	return file_viz_proto_rawDescGZIP(), []int{30, 0}
+	return file_viz_proto_rawDescGZIP(), []int{31, 0}
 }
 
 func (x *RouteTable_Row) GetRoute() string {
@@ -3179,7 +3242,7 @@ type GatewaysTable_Row struct {
 func (x *GatewaysTable_Row) Reset() {
 	*x = GatewaysTable_Row{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_viz_proto_msgTypes[44]
+		mi := &file_viz_proto_msgTypes[45]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -3192,7 +3255,7 @@ func (x *GatewaysTable_Row) String() string {
 func (*GatewaysTable_Row) ProtoMessage() {}
 
 func (x *GatewaysTable_Row) ProtoReflect() protoreflect.Message {
-	mi := &file_viz_proto_msgTypes[44]
+	mi := &file_viz_proto_msgTypes[45]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -3205,7 +3268,7 @@ func (x *GatewaysTable_Row) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use GatewaysTable_Row.ProtoReflect.Descriptor instead.
 func (*GatewaysTable_Row) Descriptor() ([]byte, []int) {
-	return file_viz_proto_rawDescGZIP(), []int{31, 0}
+	return file_viz_proto_rawDescGZIP(), []int{32, 0}
 }
 
 func (x *GatewaysTable_Row) GetNamespace() string {
@@ -3275,7 +3338,7 @@ type GatewaysResponse_Ok struct {
 func (x *GatewaysResponse_Ok) Reset() {
 	*x = GatewaysResponse_Ok{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_viz_proto_msgTypes[45]
+		mi := &file_viz_proto_msgTypes[46]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -3288,7 +3351,7 @@ func (x *GatewaysResponse_Ok) String() string {
 func (*GatewaysResponse_Ok) ProtoMessage() {}
 
 func (x *GatewaysResponse_Ok) ProtoReflect() protoreflect.Message {
-	mi := &file_viz_proto_msgTypes[45]
+	mi := &file_viz_proto_msgTypes[46]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -3301,7 +3364,7 @@ func (x *GatewaysResponse_Ok) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use GatewaysResponse_Ok.ProtoReflect.Descriptor instead.
 func (*GatewaysResponse_Ok) Descriptor() ([]byte, []int) {
-	return file_viz_proto_rawDescGZIP(), []int{33, 0}
+	return file_viz_proto_rawDescGZIP(), []int{34, 0}
 }
 
 func (x *GatewaysResponse_Ok) GetGatewaysTable() *GatewaysTable {
@@ -3548,208 +3611,217 @@ var file_viz_proto_rawDesc = []byte{
 	0x70, 0x65, 0x78, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x65, 0x61, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28,
 	0x09, 0x52, 0x04, 0x6c, 0x65, 0x61, 0x66, 0x12, 0x16, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68,
 	0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22,
-	0xe6, 0x05, 0x0a, 0x09, 0x53, 0x74, 0x61, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x3f, 0x0a,
-	0x09, 0x70, 0x6f, 0x64, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
-	0x32, 0x20, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e,
+	0x55, 0x0a, 0x0b, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x23,
+	0x0a, 0x0d, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x43, 0x6f,
+	0x75, 0x6e, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x65, 0x6e, 0x69, 0x65, 0x64, 0x5f, 0x63, 0x6f,
+	0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x64, 0x65, 0x6e, 0x69, 0x65,
+	0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x9e, 0x06, 0x0a, 0x09, 0x53, 0x74, 0x61, 0x74, 0x54,
+	0x61, 0x62, 0x6c, 0x65, 0x12, 0x3f, 0x0a, 0x09, 0x70, 0x6f, 0x64, 0x5f, 0x67, 0x72, 0x6f, 0x75,
+	0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72,
+	0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65,
+	0x2e, 0x50, 0x6f, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x48, 0x00, 0x52, 0x08, 0x70, 0x6f, 0x64,
+	0x47, 0x72, 0x6f, 0x75, 0x70, 0x1a, 0xc6, 0x05, 0x0a, 0x08, 0x50, 0x6f, 0x64, 0x47, 0x72, 0x6f,
+	0x75, 0x70, 0x12, 0x38, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
+	0x32, 0x24, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e,
 	0x53, 0x74, 0x61, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x50, 0x6f, 0x64, 0x47, 0x72, 0x6f,
-	0x75, 0x70, 0x48, 0x00, 0x52, 0x08, 0x70, 0x6f, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x1a, 0x8e,
-	0x05, 0x0a, 0x08, 0x50, 0x6f, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x38, 0x0a, 0x04, 0x72,
-	0x6f, 0x77, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6c, 0x69, 0x6e, 0x6b,
-	0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x54, 0x61, 0x62,
-	0x6c, 0x65, 0x2e, 0x50, 0x6f, 0x64, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x2e, 0x52, 0x6f, 0x77, 0x52,
-	0x04, 0x72, 0x6f, 0x77, 0x73, 0x1a, 0xc7, 0x04, 0x0a, 0x03, 0x52, 0x6f, 0x77, 0x12, 0x32, 0x0a,
-	0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x75, 0x70, 0x2e, 0x52, 0x6f, 0x77, 0x52, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x1a, 0xff, 0x04, 0x0a,
+	0x03, 0x52, 0x6f, 0x77, 0x12, 0x32, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64,
+	0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x08,
+	0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x69, 0x6d, 0x65,
+	0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74,
+	0x69, 0x6d, 0x65, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61,
+	0x74, 0x75, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,
+	0x73, 0x12, 0x28, 0x0a, 0x10, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x70, 0x6f, 0x64, 0x5f,
+	0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x6d, 0x65, 0x73,
+	0x68, 0x65, 0x64, 0x50, 0x6f, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x72,
+	0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x6f, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74,
+	0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x50,
+	0x6f, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x66, 0x61, 0x69, 0x6c, 0x65,
+	0x64, 0x5f, 0x70, 0x6f, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28,
+	0x04, 0x52, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x50, 0x6f, 0x64, 0x43, 0x6f, 0x75, 0x6e,
+	0x74, 0x12, 0x2e, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x18, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e,
+	0x42, 0x61, 0x73, 0x69, 0x63, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74,
+	0x73, 0x12, 0x33, 0x0a, 0x09, 0x74, 0x63, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x08,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e,
+	0x76, 0x69, 0x7a, 0x2e, 0x54, 0x63, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x08, 0x74, 0x63,
+	0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x3a, 0x0a, 0x08, 0x74, 0x73, 0x5f, 0x73, 0x74, 0x61,
+	0x74, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65,
+	0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x53,
+	0x70, 0x6c, 0x69, 0x74, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x07, 0x74, 0x73, 0x53, 0x74, 0x61,
+	0x74, 0x73, 0x12, 0x36, 0x0a, 0x09, 0x73, 0x72, 0x76, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18,
+	0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32,
+	0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x73,
+	0x52, 0x08, 0x73, 0x72, 0x76, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x59, 0x0a, 0x0d, 0x65, 0x72,
+	0x72, 0x6f, 0x72, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x70, 0x6f, 0x64, 0x18, 0x07, 0x20, 0x03, 0x28,
+	0x0b, 0x32, 0x35, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a,
+	0x2e, 0x53, 0x74, 0x61, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x50, 0x6f, 0x64, 0x47, 0x72,
+	0x6f, 0x75, 0x70, 0x2e, 0x52, 0x6f, 0x77, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x42, 0x79,
+	0x50, 0x6f, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73,
+	0x42, 0x79, 0x50, 0x6f, 0x64, 0x1a, 0x57, 0x0a, 0x10, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x42,
+	0x79, 0x50, 0x6f, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x76,
+	0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6c, 0x69, 0x6e,
+	0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x50, 0x6f, 0x64, 0x45, 0x72, 0x72,
+	0x6f, 0x72, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x07,
+	0x0a, 0x05, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x4b, 0x0a, 0x0c, 0x45, 0x64, 0x67, 0x65, 0x73,
+	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63,
+	0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b,
+	0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
+	0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65,
+	0x63, 0x74, 0x6f, 0x72, 0x22, 0xb2, 0x01, 0x0a, 0x0d, 0x45, 0x64, 0x67, 0x65, 0x73, 0x52, 0x65,
+	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69,
+	0x7a, 0x2e, 0x45, 0x64, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e,
+	0x4f, 0x6b, 0x48, 0x00, 0x52, 0x02, 0x6f, 0x6b, 0x12, 0x33, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f,
+	0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72,
+	0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x45,
+	0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x1a, 0x2e, 0x0a,
+	0x02, 0x4f, 0x6b, 0x12, 0x28, 0x0a, 0x05, 0x65, 0x64, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03,
+	0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69,
+	0x7a, 0x2e, 0x45, 0x64, 0x67, 0x65, 0x52, 0x05, 0x65, 0x64, 0x67, 0x65, 0x73, 0x42, 0x0a, 0x0a,
+	0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xbc, 0x01, 0x0a, 0x04, 0x45, 0x64,
+	0x67, 0x65, 0x12, 0x28, 0x0a, 0x03, 0x73, 0x72, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
 	0x16, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52,
-	0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
-	0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77,
-	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x57, 0x69, 0x6e, 0x64,
-	0x6f, 0x77, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x09, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x28, 0x0a, 0x10, 0x6d, 0x65,
-	0x73, 0x68, 0x65, 0x64, 0x5f, 0x70, 0x6f, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03,
-	0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x6d, 0x65, 0x73, 0x68, 0x65, 0x64, 0x50, 0x6f, 0x64, 0x43,
-	0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x11, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x5f,
-	0x70, 0x6f, 0x64, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52,
-	0x0f, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74,
-	0x12, 0x28, 0x0a, 0x10, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x5f, 0x70, 0x6f, 0x64, 0x5f, 0x63,
-	0x6f, 0x75, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x66, 0x61, 0x69, 0x6c,
-	0x65, 0x64, 0x50, 0x6f, 0x64, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x05, 0x73, 0x74,
-	0x61, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x69, 0x6e, 0x6b,
-	0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x42, 0x61, 0x73, 0x69, 0x63, 0x53, 0x74,
-	0x61, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x12, 0x33, 0x0a, 0x09, 0x74, 0x63,
-	0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e,
-	0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x63, 0x70,
-	0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x08, 0x74, 0x63, 0x70, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12,
-	0x3a, 0x0a, 0x08, 0x74, 0x73, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28,
-	0x0b, 0x32, 0x1f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a,
-	0x2e, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x53, 0x74, 0x61,
-	0x74, 0x73, 0x52, 0x07, 0x74, 0x73, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x59, 0x0a, 0x0d, 0x65,
-	0x72, 0x72, 0x6f, 0x72, 0x73, 0x5f, 0x62, 0x79, 0x5f, 0x70, 0x6f, 0x64, 0x18, 0x07, 0x20, 0x03,
-	0x28, 0x0b, 0x32, 0x35, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69,
-	0x7a, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x50, 0x6f, 0x64, 0x47,
-	0x72, 0x6f, 0x75, 0x70, 0x2e, 0x52, 0x6f, 0x77, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x42,
-	0x79, 0x50, 0x6f, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x65, 0x72, 0x72, 0x6f, 0x72,
-	0x73, 0x42, 0x79, 0x50, 0x6f, 0x64, 0x1a, 0x57, 0x0a, 0x10, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73,
-	0x42, 0x79, 0x50, 0x6f, 0x64, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65,
-	0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x05,
-	0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6c, 0x69,
-	0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x50, 0x6f, 0x64, 0x45, 0x72,
-	0x72, 0x6f, 0x72, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42,
-	0x07, 0x0a, 0x05, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x4b, 0x0a, 0x0c, 0x45, 0x64, 0x67, 0x65,
-	0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65,
-	0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6c, 0x69, 0x6e,
-	0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
-	0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x73, 0x65, 0x6c,
-	0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0xb2, 0x01, 0x0a, 0x0d, 0x45, 0x64, 0x67, 0x65, 0x73, 0x52,
-	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76,
-	0x69, 0x7a, 0x2e, 0x45, 0x64, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
-	0x2e, 0x4f, 0x6b, 0x48, 0x00, 0x52, 0x02, 0x6f, 0x6b, 0x12, 0x33, 0x0a, 0x05, 0x65, 0x72, 0x72,
-	0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65,
-	0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
-	0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x1a, 0x2e,
-	0x0a, 0x02, 0x4f, 0x6b, 0x12, 0x28, 0x0a, 0x05, 0x65, 0x64, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20,
-	0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76,
-	0x69, 0x7a, 0x2e, 0x45, 0x64, 0x67, 0x65, 0x52, 0x05, 0x65, 0x64, 0x67, 0x65, 0x73, 0x42, 0x0a,
-	0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xbc, 0x01, 0x0a, 0x04, 0x45,
-	0x64, 0x67, 0x65, 0x12, 0x28, 0x0a, 0x03, 0x73, 0x72, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
-	0x32, 0x16, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e,
-	0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x03, 0x73, 0x72, 0x63, 0x12, 0x28, 0x0a,
-	0x03, 0x64, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x69, 0x6e,
-	0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
-	0x63, 0x65, 0x52, 0x03, 0x64, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e,
-	0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65,
-	0x6e, 0x74, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69,
-	0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49,
-	0x64, 0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x6f, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79,
-	0x5f, 0x6d, 0x73, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x6f, 0x49, 0x64,
-	0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4d, 0x73, 0x67, 0x22, 0xe2, 0x01, 0x0a, 0x10, 0x54, 0x6f,
-	0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b,
-	0x0a, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
-	0x32, 0x1f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e,
-	0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f,
-	0x6e, 0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x74,
-	0x69, 0x6d, 0x65, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, 0x29, 0x0a, 0x04,
-	0x6e, 0x6f, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x69, 0x6e,
-	0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x48,
-	0x00, 0x52, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x74, 0x6f, 0x5f, 0x72, 0x65,
-	0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c,
+	0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x03, 0x73, 0x72, 0x63, 0x12, 0x28, 0x0a, 0x03,
+	0x64, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x69, 0x6e, 0x6b,
+	0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
+	0x65, 0x52, 0x03, 0x64, 0x73, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
+	0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e,
+	0x74, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x64,
+	0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x64,
+	0x12, 0x26, 0x0a, 0x0f, 0x6e, 0x6f, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f,
+	0x6d, 0x73, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6e, 0x6f, 0x49, 0x64, 0x65,
+	0x6e, 0x74, 0x69, 0x74, 0x79, 0x4d, 0x73, 0x67, 0x22, 0xe2, 0x01, 0x0a, 0x10, 0x54, 0x6f, 0x70,
+	0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3b, 0x0a,
+	0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x1f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52,
+	0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+	0x52, 0x08, 0x73, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x69,
+	0x6d, 0x65, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x0a, 0x74, 0x69, 0x6d, 0x65, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, 0x29, 0x0a, 0x04, 0x6e,
+	0x6f, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x6c, 0x69, 0x6e, 0x6b,
+	0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x48, 0x00,
+	0x52, 0x04, 0x6e, 0x6f, 0x6e, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x73,
+	0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6c, 0x69,
+	0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75,
+	0x72, 0x63, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x74, 0x6f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
+	0x65, 0x42, 0x0a, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0xc2, 0x01,
+	0x0a, 0x11, 0x54, 0x6f, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
+	0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69,
+	0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48,
+	0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x34, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x03,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e,
+	0x76, 0x69, 0x7a, 0x2e, 0x54, 0x6f, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73,
+	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4f, 0x6b, 0x48, 0x00, 0x52, 0x02, 0x6f, 0x6b, 0x1a, 0x36,
+	0x0a, 0x02, 0x4f, 0x6b, 0x12, 0x30, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x01,
+	0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e,
+	0x76, 0x69, 0x7a, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x06,
+	0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+	0x73, 0x65, 0x22, 0xe7, 0x01, 0x0a, 0x0a, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c,
+	0x65, 0x12, 0x30, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
+	0x1c, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52,
+	0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x52, 0x6f, 0x77, 0x52, 0x04, 0x72,
+	0x6f, 0x77, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x1a,
+	0x8a, 0x01, 0x0a, 0x03, 0x52, 0x6f, 0x77, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x74, 0x65,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x1f, 0x0a,
+	0x0b, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12, 0x1c,
+	0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x2e, 0x0a, 0x05,
+	0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x69,
+	0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x42, 0x61, 0x73, 0x69, 0x63,
+	0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x22, 0xd2, 0x02, 0x0a,
+	0x0d, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x33,
+	0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6c,
+	0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x47, 0x61, 0x74, 0x65,
+	0x77, 0x61, 0x79, 0x73, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x52, 0x6f, 0x77, 0x52, 0x04, 0x72,
+	0x6f, 0x77, 0x73, 0x1a, 0x8b, 0x02, 0x0a, 0x03, 0x52, 0x6f, 0x77, 0x12, 0x1c, 0x0a, 0x09, 0x6e,
+	0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
+	0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
+	0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a,
+	0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65,
+	0x12, 0x27, 0x0a, 0x0f, 0x70, 0x61, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69,
+	0x63, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x70, 0x61, 0x69, 0x72, 0x65,
+	0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x6c, 0x69,
+	0x76, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x12,
+	0x24, 0x0a, 0x0e, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x6d, 0x73, 0x5f, 0x70, 0x35,
+	0x30, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79,
+	0x4d, 0x73, 0x50, 0x35, 0x30, 0x12, 0x24, 0x0a, 0x0e, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79,
+	0x5f, 0x6d, 0x73, 0x5f, 0x70, 0x39, 0x35, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6c,
+	0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x4d, 0x73, 0x50, 0x39, 0x35, 0x12, 0x24, 0x0a, 0x0e, 0x6c,
+	0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x6d, 0x73, 0x5f, 0x70, 0x39, 0x39, 0x18, 0x08, 0x20,
+	0x01, 0x28, 0x04, 0x52, 0x0c, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x4d, 0x73, 0x50, 0x39,
+	0x39, 0x22, 0x8f, 0x01, 0x0a, 0x0f, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x52, 0x65,
+	0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f,
+	0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x11, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74, 0x65,
+	0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79,
+	0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x10, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61,
+	0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f,
+	0x77, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x57, 0x69, 0x6e,
+	0x64, 0x6f, 0x77, 0x22, 0xd2, 0x01, 0x0a, 0x10, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73,
+	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e,
+	0x76, 0x69, 0x7a, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70,
+	0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4f, 0x6b, 0x48, 0x00, 0x52, 0x02, 0x6f, 0x6b, 0x12, 0x33, 0x0a,
+	0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6c,
 	0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f,
-	0x75, 0x72, 0x63, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x74, 0x6f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
-	0x63, 0x65, 0x42, 0x0a, 0x0a, 0x08, 0x6f, 0x75, 0x74, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0xc2,
-	0x01, 0x0a, 0x11, 0x54, 0x6f, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70,
-	0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20,
-	0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76,
-	0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72,
-	0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x34, 0x0a, 0x02, 0x6f, 0x6b, 0x18,
-	0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32,
-	0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x54, 0x6f, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4f, 0x6b, 0x48, 0x00, 0x52, 0x02, 0x6f, 0x6b, 0x1a,
-	0x36, 0x0a, 0x02, 0x4f, 0x6b, 0x12, 0x30, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18,
-	0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32,
-	0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52,
-	0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x42, 0x0a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f,
-	0x6e, 0x73, 0x65, 0x22, 0xe7, 0x01, 0x0a, 0x0a, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62,
-	0x6c, 0x65, 0x12, 0x30, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
-	0x32, 0x1c, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e,
-	0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x52, 0x6f, 0x77, 0x52, 0x04,
-	0x72, 0x6f, 0x77, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
-	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
-	0x1a, 0x8a, 0x01, 0x0a, 0x03, 0x52, 0x6f, 0x77, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x6f, 0x75, 0x74,
-	0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x1f,
-	0x0a, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x18, 0x02, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x12,
-	0x1c, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x06, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x09, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x2e, 0x0a,
-	0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6c,
-	0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x42, 0x61, 0x73, 0x69,
-	0x63, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x22, 0xd2, 0x02,
-	0x0a, 0x0d, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x12,
-	0x33, 0x0a, 0x04, 0x72, 0x6f, 0x77, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e,
-	0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x47, 0x61, 0x74,
-	0x65, 0x77, 0x61, 0x79, 0x73, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x2e, 0x52, 0x6f, 0x77, 0x52, 0x04,
-	0x72, 0x6f, 0x77, 0x73, 0x1a, 0x8b, 0x02, 0x0a, 0x03, 0x52, 0x6f, 0x77, 0x12, 0x1c, 0x0a, 0x09,
-	0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
-	0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21,
-	0x0a, 0x0c, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d,
-	0x65, 0x12, 0x27, 0x0a, 0x0f, 0x70, 0x61, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x72, 0x76,
-	0x69, 0x63, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x70, 0x61, 0x69, 0x72,
-	0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x6c,
-	0x69, 0x76, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x76, 0x65,
-	0x12, 0x24, 0x0a, 0x0e, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x6d, 0x73, 0x5f, 0x70,
-	0x35, 0x30, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63,
-	0x79, 0x4d, 0x73, 0x50, 0x35, 0x30, 0x12, 0x24, 0x0a, 0x0e, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63,
-	0x79, 0x5f, 0x6d, 0x73, 0x5f, 0x70, 0x39, 0x35, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c,
-	0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x4d, 0x73, 0x50, 0x39, 0x35, 0x12, 0x24, 0x0a, 0x0e,
-	0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x5f, 0x6d, 0x73, 0x5f, 0x70, 0x39, 0x39, 0x18, 0x08,
-	0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x4d, 0x73, 0x50,
-	0x39, 0x39, 0x22, 0x8f, 0x01, 0x0a, 0x0f, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x52,
-	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x13, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65,
-	0x5f, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x11, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x43, 0x6c, 0x75, 0x73, 0x74,
-	0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61,
-	0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x10, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70,
-	0x61, 0x63, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x77, 0x69, 0x6e, 0x64,
-	0x6f, 0x77, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x69, 0x6d, 0x65, 0x57, 0x69,
-	0x6e, 0x64, 0x6f, 0x77, 0x22, 0xd2, 0x01, 0x0a, 0x10, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79,
-	0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x02, 0x6f, 0x6b, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32,
-	0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x52, 0x65, 0x73,
-	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x4f, 0x6b, 0x48, 0x00, 0x52, 0x02, 0x6f, 0x6b, 0x12, 0x33,
-	0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e,
-	0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x52, 0x65, 0x73,
-	0x6f, 0x75, 0x72, 0x63, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72,
-	0x72, 0x6f, 0x72, 0x1a, 0x48, 0x0a, 0x02, 0x4f, 0x6b, 0x12, 0x42, 0x0a, 0x0e, 0x67, 0x61, 0x74,
-	0x65, 0x77, 0x61, 0x79, 0x73, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x0b, 0x32, 0x1b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a,
-	0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x0d,
-	0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x0a, 0x0a,
-	0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x2a, 0x0a, 0x0b, 0x43, 0x68, 0x65,
-	0x63, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x4b, 0x10, 0x00,
-	0x12, 0x08, 0x0a, 0x04, 0x46, 0x41, 0x49, 0x4c, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52,
-	0x52, 0x4f, 0x52, 0x10, 0x02, 0x32, 0xb2, 0x04, 0x0a, 0x03, 0x41, 0x70, 0x69, 0x12, 0x54, 0x0a,
-	0x0b, 0x53, 0x74, 0x61, 0x74, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x20, 0x2e, 0x6c,
-	0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, 0x74, 0x61, 0x74,
-	0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21,
-	0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, 0x74,
-	0x61, 0x74, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
-	0x65, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x05, 0x45, 0x64, 0x67, 0x65, 0x73, 0x12, 0x1a, 0x2e, 0x6c,
-	0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x45, 0x64, 0x67, 0x65,
-	0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65,
-	0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x45, 0x64, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73,
-	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x08, 0x47, 0x61, 0x74, 0x65, 0x77,
-	0x61, 0x79, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76,
-	0x69, 0x7a, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
-	0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69,
-	0x7a, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
-	0x73, 0x65, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x09, 0x54, 0x6f, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65,
-	0x73, 0x12, 0x1e, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a,
-	0x2e, 0x54, 0x6f, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
-	0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a,
-	0x2e, 0x54, 0x6f, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
-	0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x08, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x64, 0x73,
-	0x12, 0x1d, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e,
-	0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
-	0x1e, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x4c,
-	0x69, 0x73, 0x74, 0x50, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
-	0x00, 0x12, 0x57, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
-	0x73, 0x12, 0x21, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a,
-	0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71,
-	0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e,
-	0x76, 0x69, 0x7a, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73,
-	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x09, 0x53, 0x65,
-	0x6c, 0x66, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x1e, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72,
-	0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, 0x65, 0x6c, 0x66, 0x43, 0x68, 0x65, 0x63, 0x6b,
-	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72,
-	0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, 0x65, 0x6c, 0x66, 0x43, 0x68, 0x65, 0x63, 0x6b,
-	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69,
-	0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64,
-	0x2f, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2f, 0x76, 0x69, 0x7a, 0x2f, 0x6d, 0x65,
-	0x74, 0x72, 0x69, 0x63, 0x73, 0x2d, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x76, 0x69,
-	0x7a, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x75, 0x72, 0x63, 0x65, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x48, 0x00, 0x52, 0x05, 0x65, 0x72, 0x72,
+	0x6f, 0x72, 0x1a, 0x48, 0x0a, 0x02, 0x4f, 0x6b, 0x12, 0x42, 0x0a, 0x0e, 0x67, 0x61, 0x74, 0x65,
+	0x77, 0x61, 0x79, 0x73, 0x5f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x1b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e,
+	0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x0d, 0x67,
+	0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x0a, 0x0a, 0x08,
+	0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x2a, 0x0a, 0x0b, 0x43, 0x68, 0x65, 0x63,
+	0x6b, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x06, 0x0a, 0x02, 0x4f, 0x4b, 0x10, 0x00, 0x12,
+	0x08, 0x0a, 0x04, 0x46, 0x41, 0x49, 0x4c, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52,
+	0x4f, 0x52, 0x10, 0x02, 0x32, 0xb2, 0x04, 0x0a, 0x03, 0x41, 0x70, 0x69, 0x12, 0x54, 0x0a, 0x0b,
+	0x53, 0x74, 0x61, 0x74, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x20, 0x2e, 0x6c, 0x69,
+	0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x53,
+	0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e,
+	0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, 0x74, 0x61,
+	0x74, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+	0x22, 0x00, 0x12, 0x42, 0x0a, 0x05, 0x45, 0x64, 0x67, 0x65, 0x73, 0x12, 0x1a, 0x2e, 0x6c, 0x69,
+	0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x45, 0x64, 0x67, 0x65, 0x73,
+	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72,
+	0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x45, 0x64, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70,
+	0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x08, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61,
+	0x79, 0x73, 0x12, 0x1d, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69,
+	0x7a, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+	0x74, 0x1a, 0x1e, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a,
+	0x2e, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+	0x65, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x09, 0x54, 0x6f, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73,
+	0x12, 0x1e, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e,
+	0x54, 0x6f, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x1a, 0x1f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e,
+	0x54, 0x6f, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+	0x65, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x08, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x6f, 0x64, 0x73, 0x12,
+	0x1d, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x4c,
+	0x69, 0x73, 0x74, 0x50, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e,
+	0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x4c, 0x69,
+	0x73, 0x74, 0x50, 0x6f, 0x64, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
+	0x12, 0x57, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73,
+	0x12, 0x21, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e,
+	0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2e, 0x76,
+	0x69, 0x7a, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52,
+	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x09, 0x53, 0x65, 0x6c,
+	0x66, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x1e, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64,
+	0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, 0x65, 0x6c, 0x66, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52,
+	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64,
+	0x32, 0x2e, 0x76, 0x69, 0x7a, 0x2e, 0x53, 0x65, 0x6c, 0x66, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x52,
+	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74,
+	0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x2f,
+	0x6c, 0x69, 0x6e, 0x6b, 0x65, 0x72, 0x64, 0x32, 0x2f, 0x76, 0x69, 0x7a, 0x2f, 0x6d, 0x65, 0x74,
+	0x72, 0x69, 0x63, 0x73, 0x2d, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x76, 0x69, 0x7a,
+	0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -3765,7 +3837,7 @@ func file_viz_proto_rawDescGZIP() []byte {
 }
 
 var file_viz_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
-var file_viz_proto_msgTypes = make([]protoimpl.MessageInfo, 46)
+var file_viz_proto_msgTypes = make([]protoimpl.MessageInfo, 47)
 var file_viz_proto_goTypes = []interface{}{
 	(CheckStatus)(0),                          // 0: linkerd2.viz.CheckStatus
 	(HttpMethod_Registered)(0),                // 1: linkerd2.viz.HttpMethod.Registered
@@ -3794,29 +3866,30 @@ var file_viz_proto_goTypes = []interface{}{
 	(*BasicStats)(nil),                        // 24: linkerd2.viz.BasicStats
 	(*TcpStats)(nil),                          // 25: linkerd2.viz.TcpStats
 	(*TrafficSplitStats)(nil),                 // 26: linkerd2.viz.TrafficSplitStats
-	(*StatTable)(nil),                         // 27: linkerd2.viz.StatTable
-	(*EdgesRequest)(nil),                      // 28: linkerd2.viz.EdgesRequest
-	(*EdgesResponse)(nil),                     // 29: linkerd2.viz.EdgesResponse
-	(*Edge)(nil),                              // 30: linkerd2.viz.Edge
-	(*TopRoutesRequest)(nil),                  // 31: linkerd2.viz.TopRoutesRequest
-	(*TopRoutesResponse)(nil),                 // 32: linkerd2.viz.TopRoutesResponse
-	(*RouteTable)(nil),                        // 33: linkerd2.viz.RouteTable
-	(*GatewaysTable)(nil),                     // 34: linkerd2.viz.GatewaysTable
-	(*GatewaysRequest)(nil),                   // 35: linkerd2.viz.GatewaysRequest
-	(*GatewaysResponse)(nil),                  // 36: linkerd2.viz.GatewaysResponse
-	(*Headers_Header)(nil),                    // 37: linkerd2.viz.Headers.Header
-	(*PodErrors_PodError)(nil),                // 38: linkerd2.viz.PodErrors.PodError
-	(*PodErrors_PodError_ContainerError)(nil), // 39: linkerd2.viz.PodErrors.PodError.ContainerError
-	(*StatSummaryResponse_Ok)(nil),            // 40: linkerd2.viz.StatSummaryResponse.Ok
-	(*StatTable_PodGroup)(nil),                // 41: linkerd2.viz.StatTable.PodGroup
-	(*StatTable_PodGroup_Row)(nil),            // 42: linkerd2.viz.StatTable.PodGroup.Row
-	nil,                                       // 43: linkerd2.viz.StatTable.PodGroup.Row.ErrorsByPodEntry
-	(*EdgesResponse_Ok)(nil),                  // 44: linkerd2.viz.EdgesResponse.Ok
-	(*TopRoutesResponse_Ok)(nil),              // 45: linkerd2.viz.TopRoutesResponse.Ok
-	(*RouteTable_Row)(nil),                    // 46: linkerd2.viz.RouteTable.Row
-	(*GatewaysTable_Row)(nil),                 // 47: linkerd2.viz.GatewaysTable.Row
-	(*GatewaysResponse_Ok)(nil),               // 48: linkerd2.viz.GatewaysResponse.Ok
-	(*duration.Duration)(nil),                 // 49: google.protobuf.Duration
+	(*ServerStats)(nil),                       // 27: linkerd2.viz.ServerStats
+	(*StatTable)(nil),                         // 28: linkerd2.viz.StatTable
+	(*EdgesRequest)(nil),                      // 29: linkerd2.viz.EdgesRequest
+	(*EdgesResponse)(nil),                     // 30: linkerd2.viz.EdgesResponse
+	(*Edge)(nil),                              // 31: linkerd2.viz.Edge
+	(*TopRoutesRequest)(nil),                  // 32: linkerd2.viz.TopRoutesRequest
+	(*TopRoutesResponse)(nil),                 // 33: linkerd2.viz.TopRoutesResponse
+	(*RouteTable)(nil),                        // 34: linkerd2.viz.RouteTable
+	(*GatewaysTable)(nil),                     // 35: linkerd2.viz.GatewaysTable
+	(*GatewaysRequest)(nil),                   // 36: linkerd2.viz.GatewaysRequest
+	(*GatewaysResponse)(nil),                  // 37: linkerd2.viz.GatewaysResponse
+	(*Headers_Header)(nil),                    // 38: linkerd2.viz.Headers.Header
+	(*PodErrors_PodError)(nil),                // 39: linkerd2.viz.PodErrors.PodError
+	(*PodErrors_PodError_ContainerError)(nil), // 40: linkerd2.viz.PodErrors.PodError.ContainerError
+	(*StatSummaryResponse_Ok)(nil),            // 41: linkerd2.viz.StatSummaryResponse.Ok
+	(*StatTable_PodGroup)(nil),                // 42: linkerd2.viz.StatTable.PodGroup
+	(*StatTable_PodGroup_Row)(nil),            // 43: linkerd2.viz.StatTable.PodGroup.Row
+	nil,                                       // 44: linkerd2.viz.StatTable.PodGroup.Row.ErrorsByPodEntry
+	(*EdgesResponse_Ok)(nil),                  // 45: linkerd2.viz.EdgesResponse.Ok
+	(*TopRoutesResponse_Ok)(nil),              // 46: linkerd2.viz.TopRoutesResponse.Ok
+	(*RouteTable_Row)(nil),                    // 47: linkerd2.viz.RouteTable.Row
+	(*GatewaysTable_Row)(nil),                 // 48: linkerd2.viz.GatewaysTable.Row
+	(*GatewaysResponse_Ok)(nil),               // 49: linkerd2.viz.GatewaysResponse.Ok
+	(*duration.Duration)(nil),                 // 50: google.protobuf.Duration
 }
 var file_viz_proto_depIdxs = []int32{
 	0,  // 0: linkerd2.viz.CheckResult.Status:type_name -> linkerd2.viz.CheckStatus
@@ -3824,23 +3897,23 @@ var file_viz_proto_depIdxs = []int32{
 	9,  // 2: linkerd2.viz.ListServicesResponse.services:type_name -> linkerd2.viz.Service
 	20, // 3: linkerd2.viz.ListPodsRequest.selector:type_name -> linkerd2.viz.ResourceSelection
 	12, // 4: linkerd2.viz.ListPodsResponse.pods:type_name -> linkerd2.viz.Pod
-	49, // 5: linkerd2.viz.Pod.sinceLastReport:type_name -> google.protobuf.Duration
-	49, // 6: linkerd2.viz.Pod.uptime:type_name -> google.protobuf.Duration
+	50, // 5: linkerd2.viz.Pod.sinceLastReport:type_name -> google.protobuf.Duration
+	50, // 6: linkerd2.viz.Pod.uptime:type_name -> google.protobuf.Duration
 	1,  // 7: linkerd2.viz.HttpMethod.registered:type_name -> linkerd2.viz.HttpMethod.Registered
 	2,  // 8: linkerd2.viz.Scheme.registered:type_name -> linkerd2.viz.Scheme.Registered
-	37, // 9: linkerd2.viz.Headers.headers:type_name -> linkerd2.viz.Headers.Header
-	38, // 10: linkerd2.viz.PodErrors.errors:type_name -> linkerd2.viz.PodErrors.PodError
+	38, // 9: linkerd2.viz.Headers.headers:type_name -> linkerd2.viz.Headers.Header
+	39, // 10: linkerd2.viz.PodErrors.errors:type_name -> linkerd2.viz.PodErrors.PodError
 	19, // 11: linkerd2.viz.ResourceSelection.resource:type_name -> linkerd2.viz.Resource
 	19, // 12: linkerd2.viz.ResourceError.resource:type_name -> linkerd2.viz.Resource
 	20, // 13: linkerd2.viz.StatSummaryRequest.selector:type_name -> linkerd2.viz.ResourceSelection
 	3,  // 14: linkerd2.viz.StatSummaryRequest.none:type_name -> linkerd2.viz.Empty
 	19, // 15: linkerd2.viz.StatSummaryRequest.to_resource:type_name -> linkerd2.viz.Resource
 	19, // 16: linkerd2.viz.StatSummaryRequest.from_resource:type_name -> linkerd2.viz.Resource
-	40, // 17: linkerd2.viz.StatSummaryResponse.ok:type_name -> linkerd2.viz.StatSummaryResponse.Ok
+	41, // 17: linkerd2.viz.StatSummaryResponse.ok:type_name -> linkerd2.viz.StatSummaryResponse.Ok
 	21, // 18: linkerd2.viz.StatSummaryResponse.error:type_name -> linkerd2.viz.ResourceError
-	41, // 19: linkerd2.viz.StatTable.pod_group:type_name -> linkerd2.viz.StatTable.PodGroup
+	42, // 19: linkerd2.viz.StatTable.pod_group:type_name -> linkerd2.viz.StatTable.PodGroup
 	20, // 20: linkerd2.viz.EdgesRequest.selector:type_name -> linkerd2.viz.ResourceSelection
-	44, // 21: linkerd2.viz.EdgesResponse.ok:type_name -> linkerd2.viz.EdgesResponse.Ok
+	45, // 21: linkerd2.viz.EdgesResponse.ok:type_name -> linkerd2.viz.EdgesResponse.Ok
 	21, // 22: linkerd2.viz.EdgesResponse.error:type_name -> linkerd2.viz.ResourceError
 	19, // 23: linkerd2.viz.Edge.src:type_name -> linkerd2.viz.Resource
 	19, // 24: linkerd2.viz.Edge.dst:type_name -> linkerd2.viz.Resource
@@ -3848,43 +3921,44 @@ var file_viz_proto_depIdxs = []int32{
 	3,  // 26: linkerd2.viz.TopRoutesRequest.none:type_name -> linkerd2.viz.Empty
 	19, // 27: linkerd2.viz.TopRoutesRequest.to_resource:type_name -> linkerd2.viz.Resource
 	21, // 28: linkerd2.viz.TopRoutesResponse.error:type_name -> linkerd2.viz.ResourceError
-	45, // 29: linkerd2.viz.TopRoutesResponse.ok:type_name -> linkerd2.viz.TopRoutesResponse.Ok
-	46, // 30: linkerd2.viz.RouteTable.rows:type_name -> linkerd2.viz.RouteTable.Row
-	47, // 31: linkerd2.viz.GatewaysTable.rows:type_name -> linkerd2.viz.GatewaysTable.Row
-	48, // 32: linkerd2.viz.GatewaysResponse.ok:type_name -> linkerd2.viz.GatewaysResponse.Ok
+	46, // 29: linkerd2.viz.TopRoutesResponse.ok:type_name -> linkerd2.viz.TopRoutesResponse.Ok
+	47, // 30: linkerd2.viz.RouteTable.rows:type_name -> linkerd2.viz.RouteTable.Row
+	48, // 31: linkerd2.viz.GatewaysTable.rows:type_name -> linkerd2.viz.GatewaysTable.Row
+	49, // 32: linkerd2.viz.GatewaysResponse.ok:type_name -> linkerd2.viz.GatewaysResponse.Ok
 	21, // 33: linkerd2.viz.GatewaysResponse.error:type_name -> linkerd2.viz.ResourceError
-	39, // 34: linkerd2.viz.PodErrors.PodError.container:type_name -> linkerd2.viz.PodErrors.PodError.ContainerError
-	27, // 35: linkerd2.viz.StatSummaryResponse.Ok.stat_tables:type_name -> linkerd2.viz.StatTable
-	42, // 36: linkerd2.viz.StatTable.PodGroup.rows:type_name -> linkerd2.viz.StatTable.PodGroup.Row
+	40, // 34: linkerd2.viz.PodErrors.PodError.container:type_name -> linkerd2.viz.PodErrors.PodError.ContainerError
+	28, // 35: linkerd2.viz.StatSummaryResponse.Ok.stat_tables:type_name -> linkerd2.viz.StatTable
+	43, // 36: linkerd2.viz.StatTable.PodGroup.rows:type_name -> linkerd2.viz.StatTable.PodGroup.Row
 	19, // 37: linkerd2.viz.StatTable.PodGroup.Row.resource:type_name -> linkerd2.viz.Resource
 	24, // 38: linkerd2.viz.StatTable.PodGroup.Row.stats:type_name -> linkerd2.viz.BasicStats
 	25, // 39: linkerd2.viz.StatTable.PodGroup.Row.tcp_stats:type_name -> linkerd2.viz.TcpStats
 	26, // 40: linkerd2.viz.StatTable.PodGroup.Row.ts_stats:type_name -> linkerd2.viz.TrafficSplitStats
-	43, // 41: linkerd2.viz.StatTable.PodGroup.Row.errors_by_pod:type_name -> linkerd2.viz.StatTable.PodGroup.Row.ErrorsByPodEntry
-	18, // 42: linkerd2.viz.StatTable.PodGroup.Row.ErrorsByPodEntry.value:type_name -> linkerd2.viz.PodErrors
-	30, // 43: linkerd2.viz.EdgesResponse.Ok.edges:type_name -> linkerd2.viz.Edge
-	33, // 44: linkerd2.viz.TopRoutesResponse.Ok.routes:type_name -> linkerd2.viz.RouteTable
-	24, // 45: linkerd2.viz.RouteTable.Row.stats:type_name -> linkerd2.viz.BasicStats
-	34, // 46: linkerd2.viz.GatewaysResponse.Ok.gateways_table:type_name -> linkerd2.viz.GatewaysTable
-	22, // 47: linkerd2.viz.Api.StatSummary:input_type -> linkerd2.viz.StatSummaryRequest
-	28, // 48: linkerd2.viz.Api.Edges:input_type -> linkerd2.viz.EdgesRequest
-	35, // 49: linkerd2.viz.Api.Gateways:input_type -> linkerd2.viz.GatewaysRequest
-	31, // 50: linkerd2.viz.Api.TopRoutes:input_type -> linkerd2.viz.TopRoutesRequest
-	10, // 51: linkerd2.viz.Api.ListPods:input_type -> linkerd2.viz.ListPodsRequest
-	7,  // 52: linkerd2.viz.Api.ListServices:input_type -> linkerd2.viz.ListServicesRequest
-	5,  // 53: linkerd2.viz.Api.SelfCheck:input_type -> linkerd2.viz.SelfCheckRequest
-	23, // 54: linkerd2.viz.Api.StatSummary:output_type -> linkerd2.viz.StatSummaryResponse
-	29, // 55: linkerd2.viz.Api.Edges:output_type -> linkerd2.viz.EdgesResponse
-	36, // 56: linkerd2.viz.Api.Gateways:output_type -> linkerd2.viz.GatewaysResponse
-	32, // 57: linkerd2.viz.Api.TopRoutes:output_type -> linkerd2.viz.TopRoutesResponse
-	11, // 58: linkerd2.viz.Api.ListPods:output_type -> linkerd2.viz.ListPodsResponse
-	8,  // 59: linkerd2.viz.Api.ListServices:output_type -> linkerd2.viz.ListServicesResponse
-	6,  // 60: linkerd2.viz.Api.SelfCheck:output_type -> linkerd2.viz.SelfCheckResponse
-	54, // [54:61] is the sub-list for method output_type
-	47, // [47:54] is the sub-list for method input_type
-	47, // [47:47] is the sub-list for extension type_name
-	47, // [47:47] is the sub-list for extension extendee
-	0,  // [0:47] is the sub-list for field type_name
+	27, // 41: linkerd2.viz.StatTable.PodGroup.Row.srv_stats:type_name -> linkerd2.viz.ServerStats
+	44, // 42: linkerd2.viz.StatTable.PodGroup.Row.errors_by_pod:type_name -> linkerd2.viz.StatTable.PodGroup.Row.ErrorsByPodEntry
+	18, // 43: linkerd2.viz.StatTable.PodGroup.Row.ErrorsByPodEntry.value:type_name -> linkerd2.viz.PodErrors
+	31, // 44: linkerd2.viz.EdgesResponse.Ok.edges:type_name -> linkerd2.viz.Edge
+	34, // 45: linkerd2.viz.TopRoutesResponse.Ok.routes:type_name -> linkerd2.viz.RouteTable
+	24, // 46: linkerd2.viz.RouteTable.Row.stats:type_name -> linkerd2.viz.BasicStats
+	35, // 47: linkerd2.viz.GatewaysResponse.Ok.gateways_table:type_name -> linkerd2.viz.GatewaysTable
+	22, // 48: linkerd2.viz.Api.StatSummary:input_type -> linkerd2.viz.StatSummaryRequest
+	29, // 49: linkerd2.viz.Api.Edges:input_type -> linkerd2.viz.EdgesRequest
+	36, // 50: linkerd2.viz.Api.Gateways:input_type -> linkerd2.viz.GatewaysRequest
+	32, // 51: linkerd2.viz.Api.TopRoutes:input_type -> linkerd2.viz.TopRoutesRequest
+	10, // 52: linkerd2.viz.Api.ListPods:input_type -> linkerd2.viz.ListPodsRequest
+	7,  // 53: linkerd2.viz.Api.ListServices:input_type -> linkerd2.viz.ListServicesRequest
+	5,  // 54: linkerd2.viz.Api.SelfCheck:input_type -> linkerd2.viz.SelfCheckRequest
+	23, // 55: linkerd2.viz.Api.StatSummary:output_type -> linkerd2.viz.StatSummaryResponse
+	30, // 56: linkerd2.viz.Api.Edges:output_type -> linkerd2.viz.EdgesResponse
+	37, // 57: linkerd2.viz.Api.Gateways:output_type -> linkerd2.viz.GatewaysResponse
+	33, // 58: linkerd2.viz.Api.TopRoutes:output_type -> linkerd2.viz.TopRoutesResponse
+	11, // 59: linkerd2.viz.Api.ListPods:output_type -> linkerd2.viz.ListPodsResponse
+	8,  // 60: linkerd2.viz.Api.ListServices:output_type -> linkerd2.viz.ListServicesResponse
+	6,  // 61: linkerd2.viz.Api.SelfCheck:output_type -> linkerd2.viz.SelfCheckResponse
+	55, // [55:62] is the sub-list for method output_type
+	48, // [48:55] is the sub-list for method input_type
+	48, // [48:48] is the sub-list for extension type_name
+	48, // [48:48] is the sub-list for extension extendee
+	0,  // [0:48] is the sub-list for field type_name
 }
 
 func init() { file_viz_proto_init() }
@@ -4182,7 +4256,7 @@ func file_viz_proto_init() {
 			}
 		}
 		file_viz_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*StatTable); i {
+			switch v := v.(*ServerStats); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4194,7 +4268,7 @@ func file_viz_proto_init() {
 			}
 		}
 		file_viz_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*EdgesRequest); i {
+			switch v := v.(*StatTable); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4206,7 +4280,7 @@ func file_viz_proto_init() {
 			}
 		}
 		file_viz_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*EdgesResponse); i {
+			switch v := v.(*EdgesRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4218,7 +4292,7 @@ func file_viz_proto_init() {
 			}
 		}
 		file_viz_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*Edge); i {
+			switch v := v.(*EdgesResponse); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4230,7 +4304,7 @@ func file_viz_proto_init() {
 			}
 		}
 		file_viz_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*TopRoutesRequest); i {
+			switch v := v.(*Edge); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4242,7 +4316,7 @@ func file_viz_proto_init() {
 			}
 		}
 		file_viz_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*TopRoutesResponse); i {
+			switch v := v.(*TopRoutesRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4254,7 +4328,7 @@ func file_viz_proto_init() {
 			}
 		}
 		file_viz_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*RouteTable); i {
+			switch v := v.(*TopRoutesResponse); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4266,7 +4340,7 @@ func file_viz_proto_init() {
 			}
 		}
 		file_viz_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*GatewaysTable); i {
+			switch v := v.(*RouteTable); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4278,7 +4352,7 @@ func file_viz_proto_init() {
 			}
 		}
 		file_viz_proto_msgTypes[32].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*GatewaysRequest); i {
+			switch v := v.(*GatewaysTable); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4290,7 +4364,7 @@ func file_viz_proto_init() {
 			}
 		}
 		file_viz_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*GatewaysResponse); i {
+			switch v := v.(*GatewaysRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4302,7 +4376,7 @@ func file_viz_proto_init() {
 			}
 		}
 		file_viz_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*Headers_Header); i {
+			switch v := v.(*GatewaysResponse); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4314,7 +4388,7 @@ func file_viz_proto_init() {
 			}
 		}
 		file_viz_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*PodErrors_PodError); i {
+			switch v := v.(*Headers_Header); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4326,7 +4400,7 @@ func file_viz_proto_init() {
 			}
 		}
 		file_viz_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*PodErrors_PodError_ContainerError); i {
+			switch v := v.(*PodErrors_PodError); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4338,7 +4412,7 @@ func file_viz_proto_init() {
 			}
 		}
 		file_viz_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*StatSummaryResponse_Ok); i {
+			switch v := v.(*PodErrors_PodError_ContainerError); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4350,7 +4424,7 @@ func file_viz_proto_init() {
 			}
 		}
 		file_viz_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*StatTable_PodGroup); i {
+			switch v := v.(*StatSummaryResponse_Ok); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4362,6 +4436,18 @@ func file_viz_proto_init() {
 			}
 		}
 		file_viz_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*StatTable_PodGroup); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_viz_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*StatTable_PodGroup_Row); i {
 			case 0:
 				return &v.state
@@ -4373,7 +4459,7 @@ func file_viz_proto_init() {
 				return nil
 			}
 		}
-		file_viz_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} {
+		file_viz_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*EdgesResponse_Ok); i {
 			case 0:
 				return &v.state
@@ -4385,7 +4471,7 @@ func file_viz_proto_init() {
 				return nil
 			}
 		}
-		file_viz_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} {
+		file_viz_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*TopRoutesResponse_Ok); i {
 			case 0:
 				return &v.state
@@ -4397,7 +4483,7 @@ func file_viz_proto_init() {
 				return nil
 			}
 		}
-		file_viz_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} {
+		file_viz_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*RouteTable_Row); i {
 			case 0:
 				return &v.state
@@ -4409,7 +4495,7 @@ func file_viz_proto_init() {
 				return nil
 			}
 		}
-		file_viz_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} {
+		file_viz_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*GatewaysTable_Row); i {
 			case 0:
 				return &v.state
@@ -4421,7 +4507,7 @@ func file_viz_proto_init() {
 				return nil
 			}
 		}
-		file_viz_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} {
+		file_viz_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*GatewaysResponse_Ok); i {
 			case 0:
 				return &v.state
@@ -4463,30 +4549,30 @@ func file_viz_proto_init() {
 		(*StatSummaryResponse_Ok_)(nil),
 		(*StatSummaryResponse_Error)(nil),
 	}
-	file_viz_proto_msgTypes[24].OneofWrappers = []interface{}{
+	file_viz_proto_msgTypes[25].OneofWrappers = []interface{}{
 		(*StatTable_PodGroup_)(nil),
 	}
-	file_viz_proto_msgTypes[26].OneofWrappers = []interface{}{
+	file_viz_proto_msgTypes[27].OneofWrappers = []interface{}{
 		(*EdgesResponse_Ok_)(nil),
 		(*EdgesResponse_Error)(nil),
 	}
-	file_viz_proto_msgTypes[28].OneofWrappers = []interface{}{
+	file_viz_proto_msgTypes[29].OneofWrappers = []interface{}{
 		(*TopRoutesRequest_None)(nil),
 		(*TopRoutesRequest_ToResource)(nil),
 	}
-	file_viz_proto_msgTypes[29].OneofWrappers = []interface{}{
+	file_viz_proto_msgTypes[30].OneofWrappers = []interface{}{
 		(*TopRoutesResponse_Error)(nil),
 		(*TopRoutesResponse_Ok_)(nil),
 	}
-	file_viz_proto_msgTypes[33].OneofWrappers = []interface{}{
+	file_viz_proto_msgTypes[34].OneofWrappers = []interface{}{
 		(*GatewaysResponse_Ok_)(nil),
 		(*GatewaysResponse_Error)(nil),
 	}
-	file_viz_proto_msgTypes[34].OneofWrappers = []interface{}{
+	file_viz_proto_msgTypes[35].OneofWrappers = []interface{}{
 		(*Headers_Header_ValueStr)(nil),
 		(*Headers_Header_ValueBin)(nil),
 	}
-	file_viz_proto_msgTypes[35].OneofWrappers = []interface{}{
+	file_viz_proto_msgTypes[36].OneofWrappers = []interface{}{
 		(*PodErrors_PodError_Container)(nil),
 	}
 	type x struct{}
@@ -4495,7 +4581,7 @@ func file_viz_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_viz_proto_rawDesc,
 			NumEnums:      3,
-			NumMessages:   46,
+			NumMessages:   47,
 			NumExtensions: 0,
 			NumServices:   1,
 		},
diff --git a/viz/metrics-api/prometheus.go b/viz/metrics-api/prometheus.go
index 1eb039640..a901e5e3c 100644
--- a/viz/metrics-api/prometheus.go
+++ b/viz/metrics-api/prometheus.go
@@ -24,22 +24,26 @@ type promResult struct {
 }
 
 const (
-	promGatewayAlive   = promType("QUERY_GATEWAY_ALIVE")
-	promRequests       = promType("QUERY_REQUESTS")
-	promActualRequests = promType("QUERY_ACTUAL_REQUESTS")
-	promTCPConnections = promType("QUERY_TCP_CONNECTIONS")
-	promTCPReadBytes   = promType("QUERY_TCP_READ_BYTES")
-	promTCPWriteBytes  = promType("QUERY_TCP_WRITE_BYTES")
-	promLatencyP50     = promType("0.5")
-	promLatencyP95     = promType("0.95")
-	promLatencyP99     = promType("0.99")
-
-	namespaceLabel         = model.LabelName("namespace")
-	dstNamespaceLabel      = model.LabelName("dst_namespace")
-	gatewayNameLabel       = model.LabelName("gateway_name")
-	gatewayNamespaceLabel  = model.LabelName("gateway_namespace")
-	remoteClusterNameLabel = model.LabelName("target_cluster_name")
-	authorityLabel         = model.LabelName("authority")
+	promGatewayAlive    = promType("QUERY_GATEWAY_ALIVE")
+	promRequests        = promType("QUERY_REQUESTS")
+	promAllowedRequests = promType("QUERY_ALLOWED_REQUESTS")
+	promDeniedRequests  = promType("QUERY_DENIED_REQUESTS")
+	promActualRequests  = promType("QUERY_ACTUAL_REQUESTS")
+	promTCPConnections  = promType("QUERY_TCP_CONNECTIONS")
+	promTCPReadBytes    = promType("QUERY_TCP_READ_BYTES")
+	promTCPWriteBytes   = promType("QUERY_TCP_WRITE_BYTES")
+	promLatencyP50      = promType("0.5")
+	promLatencyP95      = promType("0.95")
+	promLatencyP99      = promType("0.99")
+
+	namespaceLabel           = model.LabelName("namespace")
+	dstNamespaceLabel        = model.LabelName("dst_namespace")
+	gatewayNameLabel         = model.LabelName("gateway_name")
+	gatewayNamespaceLabel    = model.LabelName("gateway_namespace")
+	remoteClusterNameLabel   = model.LabelName("target_cluster_name")
+	authorityLabel           = model.LabelName("authority")
+	serverLabel              = model.LabelName("srv_name")
+	serverAuthorizationLabel = model.LabelName("saz_name")
 )
 
 var (
diff --git a/viz/metrics-api/proto/viz.proto b/viz/metrics-api/proto/viz.proto
index a64624adf..b41eef09b 100644
--- a/viz/metrics-api/proto/viz.proto
+++ b/viz/metrics-api/proto/viz.proto
@@ -227,6 +227,11 @@ message TrafficSplitStats {
   string apex = 2;
   string leaf = 3;
   string weight = 4;
+} 
+
+message ServerStats {
+  uint64 allowed_count = 1;
+  uint64 denied_count = 2;
 }
 
 message StatTable {
@@ -253,6 +258,7 @@ message StatTable {
       BasicStats stats = 5;
       TcpStats tcp_stats = 8;
       TrafficSplitStats ts_stats = 10;
+      ServerStats srv_stats = 11;
 
       // Stores a set of errors for each pod name. If a pod has no errors, it may be omitted.
       map<string, PodErrors> errors_by_pod = 7;
diff --git a/viz/metrics-api/stat_summary.go b/viz/metrics-api/stat_summary.go
index c53e9d3fe..a26348716 100644
--- a/viz/metrics-api/stat_summary.go
+++ b/viz/metrics-api/stat_summary.go
@@ -18,8 +18,24 @@ import (
 	kerrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/api/meta"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+var (
+	serverGroupVersionResource = schema.GroupVersionResource{
+		Group:    "policy.linkerd.io",
+		Version:  "v1alpha1",
+		Resource: "servers",
+	}
+
+	serverAuthorizationGroupVersionResource = schema.GroupVersionResource{
+		Group:    "policy.linkerd.io",
+		Version:  "v1alpha1",
+		Resource: "serverauthorizations",
+	}
 )
 
 type resourceResult struct {
@@ -59,6 +75,8 @@ const (
 
 	reqQuery             = "sum(increase(response_total%s[%s])) by (%s, classification, tls)"
 	latencyQuantileQuery = "histogram_quantile(%s, sum(irate(response_latency_ms_bucket%s[%s])) by (le, %s))"
+	httpAuthzDenyQuery   = "sum(increase(inbound_http_authz_deny_total%s[%s])) by (%s)"
+	httpAuthzAllowQuery  = "sum(increase(inbound_http_authz_allow_total%s[%s])) by (%s)"
 	tcpConnectionsQuery  = "sum(tcp_open_connections%s) by (%s)"
 	tcpReadBytesQuery    = "sum(increase(tcp_read_bytes_total%s[%s])) by (%s)"
 	tcpWriteBytesQuery   = "sum(increase(tcp_write_bytes_total%s[%s])) by (%s)"
@@ -92,6 +110,11 @@ func (s *grpcServer) StatSummary(ctx context.Context, req *pb.StatSummaryRequest
 		return statSummaryError(req, "service is not supported as a target on 'from' queries, or as a target with 'to' queries"), nil
 	}
 
+	// err if --from is added with policy resources
+	if req.GetFromResource() != nil && isPolicyResource(req.GetSelector().GetResource()) {
+		return statSummaryError(req, "'from' queries are not supported with policy resources, as they have inbound metrics only"), nil
+	}
+
 	switch req.Outbound.(type) {
 	case *pb.StatSummaryRequest_ToResource:
 		if req.Outbound.(*pb.StatSummaryRequest_ToResource).ToResource.Type == k8s.All {
@@ -126,6 +149,8 @@ func (s *grpcServer) StatSummary(ctx context.Context, req *pb.StatSummaryRequest
 				resultChan <- s.serviceResourceQuery(ctx, statReq)
 			} else if isTrafficSplitQuery(statReq.GetSelector().GetResource().GetType()) {
 				resultChan <- s.trafficSplitResourceQuery(ctx, statReq)
+			} else if isPolicyResource(statReq.GetSelector().GetResource()) {
+				resultChan <- s.policyResourceQuery(ctx, statReq)
 			} else {
 				resultChan <- s.k8sResourceQuery(ctx, statReq)
 			}
@@ -152,6 +177,15 @@ func (s *grpcServer) StatSummary(ctx context.Context, req *pb.StatSummaryRequest
 	return &rsp, nil
 }
 
+func isPolicyResource(resource *pb.Resource) bool {
+	if resource != nil {
+		if resource.GetType() == k8s.Server || resource.GetType() == k8s.ServerAuthorization {
+			return true
+		}
+	}
+	return false
+}
+
 func statSummaryError(req *pb.StatSummaryRequest, message string) *pb.StatSummaryResponse {
 	return &pb.StatSummaryResponse{
 		Response: &pb.StatSummaryResponse_Error{
@@ -366,6 +400,91 @@ func (s *grpcServer) trafficSplitResourceQuery(ctx context.Context, req *pb.Stat
 	return resourceResult{res: &rsp, err: nil}
 }
 
+func (s *grpcServer) getPolicyResourceKeys(req *pb.StatSummaryRequest) ([]rKey, error) {
+	var err error
+	var unstructuredResources *unstructured.UnstructuredList
+
+	var gvr schema.GroupVersionResource
+	if req.GetSelector().Resource.GetType() == k8s.Server {
+		gvr = serverGroupVersionResource
+	} else if req.GetSelector().Resource.GetType() == k8s.ServerAuthorization {
+		gvr = serverAuthorizationGroupVersionResource
+	}
+
+	res := req.GetSelector().GetResource()
+	labelSelector, err := getLabelSelector(req)
+	if err != nil {
+		return nil, err
+	}
+
+	if res.GetNamespace() == "" {
+		unstructuredResources, err = s.k8sAPI.DynamicClient.Resource(gvr).Namespace("").List(context.TODO(), metav1.ListOptions{LabelSelector: labelSelector.String()})
+	} else if res.GetName() == "" {
+		unstructuredResources, err = s.k8sAPI.DynamicClient.Resource(gvr).Namespace(res.GetNamespace()).List(context.TODO(), metav1.ListOptions{LabelSelector: labelSelector.String()})
+	} else {
+		var ts *unstructured.Unstructured
+		ts, err = s.k8sAPI.DynamicClient.Resource(gvr).Namespace(res.GetNamespace()).Get(context.TODO(), res.GetName(), metav1.GetOptions{})
+		if err != nil {
+			return nil, err
+		}
+		unstructuredResources = &unstructured.UnstructuredList{Items: []unstructured.Unstructured{*ts}}
+	}
+	if err != nil {
+		return nil, err
+	}
+
+	var resourceKeys []rKey
+	for _, resource := range unstructuredResources.Items {
+		// Resource Key's type should be singular and lowercased while the kind isn't
+		resourceKeys = append(resourceKeys, rKey{Namespace: resource.GetNamespace(), Type: strings.ToLower(resource.GetKind()[0:len(resource.GetKind())]), Name: resource.GetName()})
+	}
+	return resourceKeys, nil
+}
+
+func (s *grpcServer) policyResourceQuery(ctx context.Context, req *pb.StatSummaryRequest) resourceResult {
+
+	policyResources, err := s.getPolicyResourceKeys(req)
+	if err != nil {
+		return resourceResult{res: nil, err: err}
+	}
+
+	var requestMetrics map[rKey]*pb.BasicStats
+	var tcpMetrics map[rKey]*pb.TcpStats
+	var authzMetrics map[rKey]*pb.ServerStats
+	if !req.SkipStats {
+		requestMetrics, tcpMetrics, authzMetrics, err = s.getPolicyMetrics(ctx, req, req.TimeWindow)
+		if err != nil {
+			return resourceResult{res: nil, err: err}
+		}
+	}
+
+	rows := make([]*pb.StatTable_PodGroup_Row, 0)
+	for _, key := range policyResources {
+		row := pb.StatTable_PodGroup_Row{
+			Resource: &pb.Resource{
+				Name:      key.Name,
+				Namespace: key.Namespace,
+				Type:      req.GetSelector().GetResource().GetType(),
+			},
+			TimeWindow: req.TimeWindow,
+			Stats:      requestMetrics[key],
+			TcpStats:   tcpMetrics[key],
+			SrvStats:   authzMetrics[key],
+		}
+
+		rows = append(rows, &row)
+	}
+
+	rsp := pb.StatTable{
+		Table: &pb.StatTable_PodGroup_{
+			PodGroup: &pb.StatTable_PodGroup{
+				Rows: rows,
+			},
+		},
+	}
+	return resourceResult{res: &rsp, err: nil}
+}
+
 func (s *grpcServer) serviceResourceQuery(ctx context.Context, req *pb.StatSummaryRequest) resourceResult {
 
 	rows := make([]*pb.StatTable_PodGroup_Row, 0)
@@ -576,6 +695,42 @@ func buildServiceRequestLabels(req *pb.StatSummaryRequest) (labels model.LabelSe
 	return labels, groupBy
 }
 
+func buildServerRequestLabels(req *pb.StatSummaryRequest) (labels model.LabelSet, labelNames model.LabelNames) {
+	if req.GetSelector().GetResource().GetNamespace() != "" {
+		labels = labels.Merge(model.LabelSet{
+			namespaceLabel: model.LabelValue(req.GetSelector().GetResource().GetNamespace()),
+		})
+	}
+	var resourceLabel model.LabelName
+	if req.GetSelector().GetResource().GetType() == k8s.Server {
+		resourceLabel = serverLabel
+	} else if req.GetSelector().GetResource().GetType() == k8s.ServerAuthorization {
+		resourceLabel = serverAuthorizationLabel
+	}
+
+	if req.GetSelector().GetResource().GetName() != "" {
+		labels = labels.Merge(model.LabelSet{
+			resourceLabel: model.LabelValue(req.GetSelector().GetResource().GetName()),
+		})
+	}
+
+	switch out := req.Outbound.(type) {
+	case *pb.StatSummaryRequest_ToResource:
+		// if --to flag is passed, Calculate traffic sent to the policy resource
+		// with additional filtering narrowing down to the workload
+		// it is sent to.
+		labels = labels.Merge(promQueryLabels(out.ToResource))
+
+	// No FromResource case as policy metrics are all inbound
+	default:
+		// no extra labels needed
+	}
+
+	groupBy := model.LabelNames{namespaceLabel, resourceLabel}
+
+	return labels, groupBy
+}
+
 func buildTCPStatsRequestLabels(req *pb.StatSummaryRequest, reqLabels model.LabelSet) string {
 	switch req.Outbound.(type) {
 	case *pb.StatSummaryRequest_ToResource, *pb.StatSummaryRequest_FromResource:
@@ -610,7 +765,7 @@ func (s *grpcServer) getStatMetrics(ctx context.Context, req *pb.StatSummaryRequ
 		return nil, nil, err
 	}
 
-	basicStats, tcpStats := processPrometheusMetrics(req, results, groupBy)
+	basicStats, tcpStats, _ := processPrometheusMetrics(req, results, groupBy)
 	return basicStats, tcpStats, nil
 }
 
@@ -633,7 +788,7 @@ func (s *grpcServer) getTrafficSplitMetrics(ctx context.Context, req *pb.StatSum
 	if err != nil {
 		return nil, err
 	}
-	basicStats, _ := processPrometheusMetrics(req, results, groupBy) // we don't need tcpStat info for traffic split
+	basicStats, _, _ := processPrometheusMetrics(req, results, groupBy) // we don't need tcpStat info for traffic split
 
 	for rKey, basicStatsVal := range basicStats {
 		tsBasicStats[tsKey{
@@ -681,7 +836,7 @@ func (s *grpcServer) getServiceMetrics(ctx context.Context, req *pb.StatSummaryR
 		return nil, nil, err
 	}
 
-	basicStats, tcpStats := processPrometheusMetrics(req, results, groupBy)
+	basicStats, tcpStats, _ := processPrometheusMetrics(req, results, groupBy)
 
 	for rKey, basicStatsVal := range basicStats {
 
@@ -716,9 +871,43 @@ func (s *grpcServer) getServiceMetrics(ctx context.Context, req *pb.StatSummaryR
 	return dstBasicStats, dstTCPStats, nil
 }
 
-func processPrometheusMetrics(req *pb.StatSummaryRequest, results []promResult, groupBy model.LabelNames) (map[rKey]*pb.BasicStats, map[rKey]*pb.TcpStats) {
+func (s *grpcServer) getPolicyMetrics(ctx context.Context, req *pb.StatSummaryRequest, timeWindow string) (map[rKey]*pb.BasicStats, map[rKey]*pb.TcpStats, map[rKey]*pb.ServerStats, error) {
+	labels, groupBy := buildServerRequestLabels(req)
+	// Server metrics are always inbound
+	reqLabels := labels.Merge(model.LabelSet{
+		"direction": model.LabelValue("inbound"),
+	})
+
+	promQueries := make(map[promType]string)
+	if req.GetSelector().GetResource().GetType() == k8s.Server {
+		// TCP metrics are only supported with servers
+		if req.TcpStats {
+			// peer is always `src` as these are inbound metrics
+			tcpLabels := reqLabels.Merge(promPeerLabel("src"))
+			promQueries[promTCPConnections] = fmt.Sprintf(tcpConnectionsQuery, tcpLabels.String(), groupBy.String())
+			promQueries[promTCPReadBytes] = fmt.Sprintf(tcpReadBytesQuery, tcpLabels.String(), timeWindow, groupBy.String())
+			promQueries[promTCPWriteBytes] = fmt.Sprintf(tcpWriteBytesQuery, tcpLabels.String(), timeWindow, groupBy.String())
+		}
+	}
+
+	promQueries[promRequests] = fmt.Sprintf(reqQuery, reqLabels, timeWindow, groupBy.String())
+	// Use `labels` as direction isn't present with authorization metrics
+	promQueries[promAllowedRequests] = fmt.Sprintf(httpAuthzAllowQuery, labels, timeWindow, groupBy.String())
+	promQueries[promDeniedRequests] = fmt.Sprintf(httpAuthzDenyQuery, labels, timeWindow, groupBy.String())
+	quantileQueries := generateQuantileQueries(latencyQuantileQuery, reqLabels.String(), timeWindow, groupBy.String())
+	results, err := s.getPrometheusMetrics(ctx, promQueries, quantileQueries)
+	if err != nil {
+		return nil, nil, nil, err
+	}
+
+	basicStats, tcpStats, authzStats := processPrometheusMetrics(req, results, groupBy)
+	return basicStats, tcpStats, authzStats, nil
+}
+
+func processPrometheusMetrics(req *pb.StatSummaryRequest, results []promResult, groupBy model.LabelNames) (map[rKey]*pb.BasicStats, map[rKey]*pb.TcpStats, map[rKey]*pb.ServerStats) {
 	basicStats := make(map[rKey]*pb.BasicStats)
 	tcpStats := make(map[rKey]*pb.TcpStats)
+	authzStats := make(map[rKey]*pb.ServerStats)
 
 	for _, result := range results {
 		for _, sample := range result.vec {
@@ -735,6 +924,12 @@ func processPrometheusMetrics(req *pb.StatSummaryRequest, results []promResult,
 				}
 			}
 
+			addAuthzStats := func() {
+				if authzStats[resource] == nil {
+					authzStats[resource] = &pb.ServerStats{}
+				}
+			}
+
 			value := extractSampleValue(sample)
 
 			switch result.prom {
@@ -764,12 +959,17 @@ func processPrometheusMetrics(req *pb.StatSummaryRequest, results []promResult,
 			case promTCPWriteBytes:
 				addTCPStats()
 				tcpStats[resource].WriteBytesTotal = value
+			case promAllowedRequests:
+				addAuthzStats()
+				authzStats[resource].AllowedCount = value
+			case promDeniedRequests:
+				addAuthzStats()
+				authzStats[resource].DeniedCount = value
 			}
-
 		}
 	}
 
-	return basicStats, tcpStats
+	return basicStats, tcpStats, authzStats
 }
 
 func metricToKey(req *pb.StatSummaryRequest, metric model.Metric, groupBy model.LabelNames) rKey {


Test patch:
diff --git a/cli/cmd/testdata/install_controlplane_tracing_output.golden b/cli/cmd/testdata/install_controlplane_tracing_output.golden
index 72532d732..16eb07999 100644
--- a/cli/cmd/testdata/install_controlplane_tracing_output.golden
+++ b/cli/cmd/testdata/install_controlplane_tracing_output.golden
@@ -1842,9 +1842,8 @@ spec:
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=cluster.local
         - --default-policy=all-unauthenticated
-        env:
-        - name: RUST_LOG
-          value: "linkerd=info,warn"
+        - --log-level=linkerd=info,warn
+        - --log-format=plain
         image: cr.l5d.io/linkerd/policy-controller:install-control-plane-version
         imagePullPolicy: IfNotPresent
         livenessProbe:
diff --git a/cli/cmd/testdata/install_custom_domain.golden b/cli/cmd/testdata/install_custom_domain.golden
index b6ff3281d..b7b612178 100644
--- a/cli/cmd/testdata/install_custom_domain.golden
+++ b/cli/cmd/testdata/install_custom_domain.golden
@@ -1840,9 +1840,8 @@ spec:
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=cluster.local
         - --default-policy=all-unauthenticated
-        env:
-        - name: RUST_LOG
-          value: "linkerd=info,warn"
+        - --log-level=linkerd=info,warn
+        - --log-format=plain
         image: cr.l5d.io/linkerd/policy-controller:install-control-plane-version
         imagePullPolicy: IfNotPresent
         livenessProbe:
diff --git a/cli/cmd/testdata/install_custom_registry.golden b/cli/cmd/testdata/install_custom_registry.golden
index 26d2b50c5..64ded8c74 100644
--- a/cli/cmd/testdata/install_custom_registry.golden
+++ b/cli/cmd/testdata/install_custom_registry.golden
@@ -1840,9 +1840,8 @@ spec:
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=cluster.local
         - --default-policy=all-unauthenticated
-        env:
-        - name: RUST_LOG
-          value: "linkerd=info,warn"
+        - --log-level=linkerd=info,warn
+        - --log-format=plain
         image: my.custom.registry/linkerd-io/policy-controller:install-control-plane-version
         imagePullPolicy: IfNotPresent
         livenessProbe:
diff --git a/cli/cmd/testdata/install_default.golden b/cli/cmd/testdata/install_default.golden
index d35481216..4704529a0 100644
--- a/cli/cmd/testdata/install_default.golden
+++ b/cli/cmd/testdata/install_default.golden
@@ -1840,9 +1840,8 @@ spec:
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=cluster.local
         - --default-policy=all-unauthenticated
-        env:
-        - name: RUST_LOG
-          value: "linkerd=info,warn"
+        - --log-level=linkerd=info,warn
+        - --log-format=plain
         image: cr.l5d.io/linkerd/policy-controller:install-control-plane-version
         imagePullPolicy: IfNotPresent
         livenessProbe:
diff --git a/cli/cmd/testdata/install_default_override_dst_get_nets.golden b/cli/cmd/testdata/install_default_override_dst_get_nets.golden
index 27bdf5fa7..e74c7f53f 100644
--- a/cli/cmd/testdata/install_default_override_dst_get_nets.golden
+++ b/cli/cmd/testdata/install_default_override_dst_get_nets.golden
@@ -1840,9 +1840,8 @@ spec:
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.0.0.0/8
         - --identity-domain=cluster.local
         - --default-policy=all-unauthenticated
-        env:
-        - name: RUST_LOG
-          value: "linkerd=info,warn"
+        - --log-level=linkerd=info,warn
+        - --log-format=plain
         image: cr.l5d.io/linkerd/policy-controller:install-control-plane-version
         imagePullPolicy: IfNotPresent
         livenessProbe:
diff --git a/cli/cmd/testdata/install_ha_output.golden b/cli/cmd/testdata/install_ha_output.golden
index fb9972f60..8f0abaf09 100644
--- a/cli/cmd/testdata/install_ha_output.golden
+++ b/cli/cmd/testdata/install_ha_output.golden
@@ -1964,9 +1964,8 @@ spec:
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=cluster.local
         - --default-policy=all-unauthenticated
-        env:
-        - name: RUST_LOG
-          value: "linkerd=info,warn"
+        - --log-level=linkerd=info,warn
+        - --log-format=plain
         image: cr.l5d.io/linkerd/policy-controller:install-control-plane-version
         imagePullPolicy: IfNotPresent
         livenessProbe:
diff --git a/cli/cmd/testdata/install_ha_with_overrides_output.golden b/cli/cmd/testdata/install_ha_with_overrides_output.golden
index 7bf0eb6e7..9629edd18 100644
--- a/cli/cmd/testdata/install_ha_with_overrides_output.golden
+++ b/cli/cmd/testdata/install_ha_with_overrides_output.golden
@@ -1964,9 +1964,8 @@ spec:
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=cluster.local
         - --default-policy=all-unauthenticated
-        env:
-        - name: RUST_LOG
-          value: "linkerd=info,warn"
+        - --log-level=linkerd=info,warn
+        - --log-format=plain
         image: cr.l5d.io/linkerd/policy-controller:install-control-plane-version
         imagePullPolicy: IfNotPresent
         livenessProbe:
diff --git a/cli/cmd/testdata/install_heartbeat_disabled_output.golden b/cli/cmd/testdata/install_heartbeat_disabled_output.golden
index 4f400086c..4e892a3c4 100644
--- a/cli/cmd/testdata/install_heartbeat_disabled_output.golden
+++ b/cli/cmd/testdata/install_heartbeat_disabled_output.golden
@@ -1771,9 +1771,8 @@ spec:
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=cluster.local
         - --default-policy=all-unauthenticated
-        env:
-        - name: RUST_LOG
-          value: "linkerd=info,warn"
+        - --log-level=linkerd=info,warn
+        - --log-format=plain
         image: cr.l5d.io/linkerd/policy-controller:install-control-plane-version
         imagePullPolicy: IfNotPresent
         livenessProbe:
diff --git a/cli/cmd/testdata/install_helm_output.golden b/cli/cmd/testdata/install_helm_output.golden
index b17be605e..89dddad3c 100644
--- a/cli/cmd/testdata/install_helm_output.golden
+++ b/cli/cmd/testdata/install_helm_output.golden
@@ -1833,9 +1833,8 @@ spec:
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=test.trust.domain
         - --default-policy=all-unauthenticated
-        env:
-        - name: RUST_LOG
-          value: "linkerd=info,warn"
+        - --log-level=linkerd=info,warn
+        - --log-format=plain
         image: cr.l5d.io/linkerd/policy-controller:linkerd-version
         imagePullPolicy: IfNotPresent
         livenessProbe:
diff --git a/cli/cmd/testdata/install_helm_output_ha.golden b/cli/cmd/testdata/install_helm_output_ha.golden
index 615628a32..64b054a9b 100644
--- a/cli/cmd/testdata/install_helm_output_ha.golden
+++ b/cli/cmd/testdata/install_helm_output_ha.golden
@@ -1957,9 +1957,8 @@ spec:
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=test.trust.domain
         - --default-policy=all-unauthenticated
-        env:
-        - name: RUST_LOG
-          value: "linkerd=info,warn"
+        - --log-level=linkerd=info,warn
+        - --log-format=plain
         image: cr.l5d.io/linkerd/policy-controller:linkerd-version
         imagePullPolicy: IfNotPresent
         livenessProbe:
diff --git a/cli/cmd/testdata/install_helm_output_ha_labels.golden b/cli/cmd/testdata/install_helm_output_ha_labels.golden
index a6ac843f4..9996c8537 100644
--- a/cli/cmd/testdata/install_helm_output_ha_labels.golden
+++ b/cli/cmd/testdata/install_helm_output_ha_labels.golden
@@ -1969,9 +1969,8 @@ spec:
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=test.trust.domain
         - --default-policy=all-unauthenticated
-        env:
-        - name: RUST_LOG
-          value: "linkerd=info,warn"
+        - --log-level=linkerd=info,warn
+        - --log-format=plain
         image: cr.l5d.io/linkerd/policy-controller:linkerd-version
         imagePullPolicy: IfNotPresent
         livenessProbe:
diff --git a/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden b/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden
index a7178f86a..e147104cf 100644
--- a/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden
+++ b/cli/cmd/testdata/install_helm_output_ha_namespace_selector.golden
@@ -1957,9 +1957,8 @@ spec:
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=test.trust.domain
         - --default-policy=all-unauthenticated
-        env:
-        - name: RUST_LOG
-          value: "linkerd=info,warn"
+        - --log-level=linkerd=info,warn
+        - --log-format=plain
         image: cr.l5d.io/linkerd/policy-controller:linkerd-version
         imagePullPolicy: IfNotPresent
         livenessProbe:
diff --git a/cli/cmd/testdata/install_no_init_container.golden b/cli/cmd/testdata/install_no_init_container.golden
index 830d1d3c4..9a4b023e3 100644
--- a/cli/cmd/testdata/install_no_init_container.golden
+++ b/cli/cmd/testdata/install_no_init_container.golden
@@ -1802,9 +1802,8 @@ spec:
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=cluster.local
         - --default-policy=all-unauthenticated
-        env:
-        - name: RUST_LOG
-          value: "linkerd=info,warn"
+        - --log-level=linkerd=info,warn
+        - --log-format=plain
         image: cr.l5d.io/linkerd/policy-controller:install-control-plane-version
         imagePullPolicy: IfNotPresent
         livenessProbe:
diff --git a/cli/cmd/testdata/install_output.golden b/cli/cmd/testdata/install_output.golden
index 526a4d906..45cc7ef62 100644
--- a/cli/cmd/testdata/install_output.golden
+++ b/cli/cmd/testdata/install_output.golden
@@ -1844,9 +1844,8 @@ spec:
         - --cluster-networks=ClusterNetworks
         - --identity-domain=cluster.local
         - --default-policy=default-allow-policy
-        env:
-        - name: RUST_LOG
-          value: "log-level"
+        - --log-level=log-level
+        - --log-format=ControllerLogFormat
         image: PolicyControllerImageName:PolicyControllerVersion
         imagePullPolicy: ImagePullPolicy
         livenessProbe:
diff --git a/cli/cmd/testdata/install_proxy_ignores.golden b/cli/cmd/testdata/install_proxy_ignores.golden
index 55454caa9..cb79fcec8 100644
--- a/cli/cmd/testdata/install_proxy_ignores.golden
+++ b/cli/cmd/testdata/install_proxy_ignores.golden
@@ -1840,9 +1840,8 @@ spec:
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=cluster.local
         - --default-policy=all-unauthenticated
-        env:
-        - name: RUST_LOG
-          value: "linkerd=info,warn"
+        - --log-level=linkerd=info,warn
+        - --log-format=plain
         image: cr.l5d.io/linkerd/policy-controller:install-control-plane-version
         imagePullPolicy: IfNotPresent
         livenessProbe:
diff --git a/cli/cmd/testdata/install_values_file.golden b/cli/cmd/testdata/install_values_file.golden
index d52f3602b..4af482619 100644
--- a/cli/cmd/testdata/install_values_file.golden
+++ b/cli/cmd/testdata/install_values_file.golden
@@ -1826,9 +1826,8 @@ spec:
         - --cluster-networks=10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
         - --identity-domain=example.com
         - --default-policy=all-unauthenticated
-        env:
-        - name: RUST_LOG
-          value: "linkerd=info,warn"
+        - --log-level=linkerd=info,warn
+        - --log-format=plain
         image: cr.l5d.io/linkerd/policy-controller:install-control-plane-version
         imagePullPolicy: IfNotPresent
         livenessProbe:
diff --git a/controller/k8s/test_helper.go b/controller/k8s/test_helper.go
index 2b8f3aa95..ef8adc655 100644
--- a/controller/k8s/test_helper.go
+++ b/controller/k8s/test_helper.go
@@ -13,6 +13,7 @@ func NewFakeAPI(configs ...string) (*API, error) {
 
 	return NewAPI(
 		clientSet,
+		nil,
 		spClientSet,
 		tsClientSet,
 		CJ,
diff --git a/pkg/healthcheck/healthcheck_test.go b/pkg/healthcheck/healthcheck_test.go
index 371f84ab2..936df5139 100644
--- a/pkg/healthcheck/healthcheck_test.go
+++ b/pkg/healthcheck/healthcheck_test.go
@@ -3259,306 +3259,330 @@ func TestCheckOpaquePortAnnotations(t *testing.T) {
 apiVersion: v1
 kind: Service
 metadata:
-  name: test-service-1
+  name: svc
   namespace: test-ns
+  annotations:
+    config.linkerd.io/opaque-ports: "9200"
 spec:
-  ports:
-    - name: elasticsearch
-      port: 9200
-      protocol: TCP
-      targetPort: 9200
   selector:
-    service: service-1
+    app: test
+  ports:
+  - name: test
+    port: 9200
+    targetPort: 9200
 `,
 				`
 apiVersion: v1
 kind: Pod
 metadata:
-  name: my-service-deployment
+  name: pod
   namespace: test-ns
   labels:
-    service: service-1
+    app: test
+  annotations:
+    config.linkerd.io/opaque-ports: "9200"
 spec:
   containers:
+  - name: test
+    image: test
+    ports:
     - name: test
-      image: "test-service"
+      containerPort: 9200
 `,
 				`
 apiVersion: v1
 kind: Endpoints
 metadata:
-  annotations:
-    endpoints.kubernetes.io/last-change-trigger-time: "2021-06-08T08:38:16Z"
-  creationTimestamp: "2021-06-08T08:38:03Z"
-  labels:
-    service: test-service-1
-  name: test-service-1
+  name: svc
   namespace: test-ns
 subsets:
 - addresses:
   - ip: 10.244.3.12
-    nodeName: nodename-1
+    nodeName: nod
     targetRef:
       kind: Pod
-      name: my-service-deployment
+      name: pod
       namespace: test-ns
-      resourceVersion: "20661"
-      uid: b37782aa-1458-4153-8399-dabc2b29aaae
   ports:
-  - name: http-port
-    port: 8080
+  - name: test
+    port: 9200
     protocol: TCP
 `,
 			},
-			expected: nil,
 		},
 		{
 			resources: []string{`
 apiVersion: v1
 kind: Service
 metadata:
-  name: test-service-1
+  name: svc
   namespace: test-ns
-  annotations:
-    config.linkerd.io/opaque-ports: "9200"
 spec:
-  ports:
-    - name: elasticsearch
-      port: 9200
-      protocol: TCP
-      targetPort: 9200
   selector:
-    service: service-1
+    app: test
+  ports:
+  - name: http
+    port: 9200
+    targetPort: 9200
 `,
 				`
 apiVersion: v1
 kind: Pod
 metadata:
-  name: my-service-deployment
+  name: pod
   namespace: test-ns
-  service: service-1
   labels:
-    service: service-1
+    app: test
   annotations:
     config.linkerd.io/opaque-ports: "9200"
 spec:
   containers:
+  - name: test
+    image: test
+    ports:
     - name: test
-      image: "test-service"
+      containerPort: 9200
 `,
 				`
 apiVersion: v1
 kind: Endpoints
 metadata:
-  annotations:
-    endpoints.kubernetes.io/last-change-trigger-time: "2021-06-08T08:38:16Z"
-  creationTimestamp: "2021-06-08T08:38:03Z"
-  labels:
-    service: test-service-1
-  name: test-service-1
+  name: svc
   namespace: test-ns
 subsets:
 - addresses:
   - ip: 10.244.3.12
-    nodeName: nodename-1
+    nodeName: nod
     targetRef:
       kind: Pod
-      name: my-service-deployment
+      name: pod
       namespace: test-ns
-      resourceVersion: "20661"
-      uid: b37782aa-1458-4153-8399-dabc2b29aaae
   ports:
-  - name: http-port
-    port: 8080
-    protocol: TCP			
+  - name: test
+    port: 9200
+    protocol: TCP
 `,
 			},
-			expected: nil,
+			expected: fmt.Errorf("\t* service svc targets the opaque port 9200 through 9200; add 9200 to its config.linkerd.io/opaque-ports annotation"),
 		},
 		{
 			resources: []string{`
 apiVersion: v1
 kind: Service
 metadata:
-  name: test-service-1
+  name: svc
   namespace: test-ns
+  annotations:
+    config.linkerd.io/opaque-ports: "9200"
 spec:
-  ports:
-    - name: http
-      port: 9200
-      protocol: TCP
-      targetPort: 9200
   selector:
-    service: service-1
+    app: test
+  ports:
+  - name: test
+    port: 9200
+    targetPort: 9200
 `,
 				`
 apiVersion: v1
 kind: Pod
 metadata:
-  name: my-service-deployment
+  name: pod
   namespace: test-ns
-  service: service-1
   labels:
-    service: service-1
-  annotations:
-    config.linkerd.io/opaque-ports: "9200"
+    app: test
 spec:
   containers:
+  - name: test
+    image: test
+    ports:
     - name: test
-      image: "test-service"
+      containerPort: 9200
 `,
 				`
 apiVersion: v1
 kind: Endpoints
 metadata:
-  annotations:
-    endpoints.kubernetes.io/last-change-trigger-time: "2021-06-08T08:38:16Z"
-  creationTimestamp: "2021-06-08T08:38:03Z"
-  labels:
-    service: test-service-1
-  name: test-service-1
+  name: svc
   namespace: test-ns
 subsets:
 - addresses:
   - ip: 10.244.3.12
-    nodeName: nodename-1
+    nodeName: nod
     targetRef:
       kind: Pod
-      name: my-service-deployment
+      name: pod
       namespace: test-ns
-      resourceVersion: "20661"
-      uid: b37782aa-1458-4153-8399-dabc2b29aaae
   ports:
-  - name: http-port
-    port: 8080
+  - name: test
+    port: 9200
     protocol: TCP
 `,
 			},
-			expected: fmt.Errorf("\t* pod/my-service-deployment has the annotation %s but service/test-service-1 doesn't", k8s.ProxyOpaquePortsAnnotation),
+			expected: fmt.Errorf("\t* service svc expects 9200 to be opaque; add it to pod pod's config.linkerd.io/opaque-ports annotation"),
 		},
 		{
 			resources: []string{`
 apiVersion: v1
 kind: Service
 metadata:
-  name: test-service-1
+  name: svc
   namespace: test-ns
   annotations:
     config.linkerd.io/opaque-ports: "9200"
 spec:
+  selector:
+    app: test
   ports:
-    - name: http
+    - name: test
       port: 9200
-      protocol: TCP
       targetPort: 9200
-  selector:
-    service: service-1
 `,
 				`
 apiVersion: v1
 kind: Pod
 metadata:
-  name: my-service-deployment
+  name: pod
   namespace: test-ns
-  service: service-1
   labels:
-    service: service-1
+    app: test
+  annotations:
+    config.linkerd.io/opaque-ports: "9300"
 spec:
   containers:
+  - name: test
+    image: test
+    ports:
     - name: test
-      image: "test-service"
+      containerPort: 9300
 `,
 				`
 apiVersion: v1
 kind: Endpoints
 metadata:
-  annotations:
-    endpoints.kubernetes.io/last-change-trigger-time: "2021-06-08T08:38:16Z"
-  creationTimestamp: "2021-06-08T08:38:03Z"
-  labels:
-    service: test-service-1
-  name: test-service-1
+  name: svc
   namespace: test-ns
 subsets:
 - addresses:
   - ip: 10.244.3.12
-    nodeName: nodename-1
+    nodeName: node
     targetRef:
       kind: Pod
-      name: my-service-deployment
+      name: pod
       namespace: test-ns
-      resourceVersion: "20661"
-      uid: b37782aa-1458-4153-8399-dabc2b29aaae
   ports:
-  - name: http-port
-    port: 8080
-    protocol: TCP		
+  - name: test
+    port: 9200
+    protocol: TCP
 `,
 			},
-			expected: fmt.Errorf("\t* service/test-service-1 has the annotation %s but pod/my-service-deployment doesn't", k8s.ProxyOpaquePortsAnnotation),
 		},
 		{
 			resources: []string{`
 apiVersion: v1
 kind: Service
 metadata:
-  name: test-service-1
+  name: svc
   namespace: test-ns
-  annotations:
-    config.linkerd.io/opaque-ports: "9200"
 spec:
-  ports:
-    - name: elasticsearch
-      port: 9200
-      protocol: TCP
-      targetPort: 9200
   selector:
-    service: service-1
+    app: test
+  ports:
+  - name: test
+    port: 1002
+    targetPort: 2002
 `,
 				`
 apiVersion: v1
 kind: Pod
 metadata:
-  name: my-service-deployment
+  name: pod
   namespace: test-ns
-  service: service-1
-  labels:
-    service: service-1
   annotations:
-    config.linkerd.io/opaque-ports: "9300"
+    config.linkerd.io/opaque-ports: "2002"
+  labels:
+    app: test
 spec:
   containers:
+  - name: test
+    image: test
+    ports:
     - name: test
-      image: "test-service"
+      containerPort: 2002
 `,
 				`
 apiVersion: v1
 kind: Endpoints
 metadata:
+  name: svc
+  namespace: test-ns
+subsets:
+- addresses:
+  - ip: 10.42.0.111
+    nodeName: node
+    targetRef:
+      kind: Pod
+      name: pod
+  ports:
+  - name: test
+    port: 2002
+    protocol: TCP
+`,
+			},
+			expected: fmt.Errorf("\t* service svc targets the opaque port 2002 through 1002; add 1002 to its config.linkerd.io/opaque-ports annotation"),
+		},
+		{
+			resources: []string{`
+apiVersion: v1
+kind: Service
+metadata:
+  name: svc
+  namespace: test-ns
+spec:
+  selector:
+    app: test
+  ports:
+  - name: test
+    port: 1003
+    targetPort: pod-test
+`,
+				`
+apiVersion: v1
+kind: Pod
+metadata:
+  name: pod
+  namespace: test-ns
   annotations:
-    endpoints.kubernetes.io/last-change-trigger-time: "2021-06-08T08:38:16Z"
-  creationTimestamp: "2021-06-08T08:38:03Z"
+    config.linkerd.io/opaque-ports: "2003"
   labels:
-    service: test-service-1
-  name: test-service-1
+    app: test
+spec:
+  containers:
+  - name: test
+    image: test
+    ports:
+    - name: pod-test
+      containerPort: 2003
+`,
+				`
+apiVersion: v1
+kind: Endpoints
+metadata:
+  name: svc
   namespace: test-ns
 subsets:
 - addresses:
-  - ip: 10.244.3.12
-    nodeName: nodename-1
+  - ip: 10.42.0.112
+    nodeName: node
     targetRef:
       kind: Pod
-      name: my-service-deployment
-      namespace: test-ns
-      resourceVersion: "20661"
-      uid: b37782aa-1458-4153-8399-dabc2b29aaae
+      name: pod
   ports:
-  - name: http-port
-    port: 8080
+  - name: test
+    port: 2003
     protocol: TCP
 `,
 			},
-			expected: fmt.Errorf("\t* pod/my-service-deployment and service/test-service-1 have the annotation %s but values don't match", k8s.ProxyOpaquePortsAnnotation),
+			expected: fmt.Errorf("\t* service svc targets the opaque port pod-test through 1003; add 1003 to its config.linkerd.io/opaque-ports annotation"),
 		},
 	}
 
@@ -3578,6 +3602,9 @@ subsets:
 					t.Fatalf("Expected error: %s, received: %s", tc.expected, err)
 				}
 			}
+			if err != nil && tc.expected == nil {
+				t.Fatalf("Did not expect error but got: %s", err.Error())
+			}
 		})
 	}
 }
diff --git a/test/integration/stat/stat_test.go b/test/integration/stat/stat_test.go
index 9dea7f5c8..2d471ea7e 100644
--- a/test/integration/stat/stat_test.go
+++ b/test/integration/stat/stat_test.go
@@ -71,6 +71,7 @@ func TestCliStatForLinkerdNamespace(t *testing.T) {
 		args         []string
 		expectedRows map[string]string
 		status       string
+		isAuthority  bool
 	}{
 		{
 			args: []string{"viz", "stat", "deploy", "-n", TestHelper.GetLinkerdNamespace()},
@@ -117,12 +118,14 @@ func TestCliStatForLinkerdNamespace(t *testing.T) {
 			expectedRows: map[string]string{
 				prometheusAuthority: "-",
 			},
+			isAuthority: true,
 		},
 		{
 			args: []string{"viz", "stat", "au", "-n", TestHelper.GetVizNamespace(), "--to", fmt.Sprintf("po/%s", prometheusPod), "--to-namespace", prometheusNamespace},
 			expectedRows: map[string]string{
 				prometheusAuthority: "-",
 			},
+			isAuthority: true,
 		},
 	}
 
@@ -131,6 +134,7 @@ func TestCliStatForLinkerdNamespace(t *testing.T) {
 			args         []string
 			expectedRows map[string]string
 			status       string
+			isAuthority  bool
 		}{
 			{
 				args: []string{"viz", "stat", "deploy", "-n", TestHelper.GetVizNamespace()},
@@ -162,6 +166,7 @@ func TestCliStatForLinkerdNamespace(t *testing.T) {
 			args         []string
 			expectedRows map[string]string
 			status       string
+			isAuthority  bool
 		}{
 			{
 				args: []string{"viz", "stat", "deploy", "-n", TestHelper.GetVizNamespace()},
@@ -211,6 +216,7 @@ func TestCliStatForLinkerdNamespace(t *testing.T) {
 			args         []string
 			expectedRows map[string]string
 			status       string
+			isAuthority  bool
 		}{
 			{
 				args: []string{"viz", "stat", "svc", "-n", prefixedNs},
@@ -242,6 +248,9 @@ func TestCliStatForLinkerdNamespace(t *testing.T) {
 					}
 
 					expectedColumnCount := 8
+					if tt.isAuthority {
+						expectedColumnCount = 7
+					}
 					if tt.status != "" {
 						expectedColumnCount++
 					}
@@ -251,7 +260,7 @@ func TestCliStatForLinkerdNamespace(t *testing.T) {
 					}
 
 					for name, meshed := range tt.expectedRows {
-						if err := validateRowStats(name, meshed, tt.status, rowStats); err != nil {
+						if err := validateRowStats(name, meshed, tt.status, rowStats, tt.isAuthority); err != nil {
 							return err
 						}
 					}
@@ -266,7 +275,7 @@ func TestCliStatForLinkerdNamespace(t *testing.T) {
 	})
 }
 
-func validateRowStats(name, expectedMeshCount, expectedStatus string, rowStats map[string]*testutil.RowStat) error {
+func validateRowStats(name, expectedMeshCount, expectedStatus string, rowStats map[string]*testutil.RowStat, isAuthority bool) error {
 	stat, ok := rowStats[name]
 	if !ok {
 		return fmt.Errorf("No stats found for [%s]", name)
@@ -308,7 +317,7 @@ func validateRowStats(name, expectedMeshCount, expectedStatus string, rowStats m
 			name, stat.P99Latency)
 	}
 
-	if stat.TCPOpenConnections != "-" {
+	if stat.TCPOpenConnections != "-" && !isAuthority {
 		_, err := strconv.Atoi(stat.TCPOpenConnections)
 		if err != nil {
 			return fmt.Errorf("Error parsing number of TCP connections [%s]: %s", stat.TCPOpenConnections, err.Error())
diff --git a/testutil/test_helper.go b/testutil/test_helper.go
index 10073ff29..dea3c19c4 100644
--- a/testutil/test_helper.go
+++ b/testutil/test_helper.go
@@ -729,7 +729,10 @@ func ParseRows(out string, expectedRowCount, expectedColumnCount int) (map[strin
 		rowStats[fields[0]].P50Latency = fields[4+i]
 		rowStats[fields[0]].P95Latency = fields[5+i]
 		rowStats[fields[0]].P99Latency = fields[6+i]
-		rowStats[fields[0]].TCPOpenConnections = fields[7+i]
+
+		if 7+i < len(fields) {
+			rowStats[fields[0]].TCPOpenConnections = fields[7+i]
+		}
 	}
 
 	return rowStats, nil
diff --git a/viz/cmd/testdata/install_default.golden b/viz/cmd/testdata/install_default.golden
index b31e1ea56..3c83fe4ad 100644
--- a/viz/cmd/testdata/install_default.golden
+++ b/viz/cmd/testdata/install_default.golden
@@ -38,6 +38,9 @@ rules:
 - apiGroups: ["split.smi-spec.io"]
   resources: ["trafficsplits"]
   verbs: ["list", "get", "watch"]
+- apiGroups: ["policy.linkerd.io"]
+  resources: ["servers", "serverauthorizations"]
+  verbs: ["list", "get"]
 ---
 kind: ClusterRoleBinding
 apiVersion: rbac.authorization.k8s.io/v1
@@ -434,7 +437,7 @@ spec:
   template:
     metadata:
       annotations:
-        checksum/config: 0d5b035f4d141dc2c13e1f89046de78fe0fb1208075734c3977400b866f2db51
+        checksum/config: 951a7b61ff59229914ca604c671353e5fdc0eda710bde2bfbb50c9225225babe
         linkerd.io/created-by: linkerd/helm dev-undefined
       labels:
         linkerd.io/extension: viz
diff --git a/viz/cmd/testdata/install_default_overrides.golden b/viz/cmd/testdata/install_default_overrides.golden
index f6f231388..82abf79ab 100644
--- a/viz/cmd/testdata/install_default_overrides.golden
+++ b/viz/cmd/testdata/install_default_overrides.golden
@@ -38,6 +38,9 @@ rules:
 - apiGroups: ["split.smi-spec.io"]
   resources: ["trafficsplits"]
   verbs: ["list", "get", "watch"]
+- apiGroups: ["policy.linkerd.io"]
+  resources: ["servers", "serverauthorizations"]
+  verbs: ["list", "get"]
 ---
 kind: ClusterRoleBinding
 apiVersion: rbac.authorization.k8s.io/v1
@@ -434,7 +437,7 @@ spec:
   template:
     metadata:
       annotations:
-        checksum/config: 0d5b035f4d141dc2c13e1f89046de78fe0fb1208075734c3977400b866f2db51
+        checksum/config: 951a7b61ff59229914ca604c671353e5fdc0eda710bde2bfbb50c9225225babe
         linkerd.io/created-by: linkerd/helm dev-undefined
       labels:
         linkerd.io/extension: viz
diff --git a/viz/cmd/testdata/install_grafana_disabled.golden b/viz/cmd/testdata/install_grafana_disabled.golden
index bd2ec786e..da80d2534 100644
--- a/viz/cmd/testdata/install_grafana_disabled.golden
+++ b/viz/cmd/testdata/install_grafana_disabled.golden
@@ -38,6 +38,9 @@ rules:
 - apiGroups: ["split.smi-spec.io"]
   resources: ["trafficsplits"]
   verbs: ["list", "get", "watch"]
+- apiGroups: ["policy.linkerd.io"]
+  resources: ["servers", "serverauthorizations"]
+  verbs: ["list", "get"]
 ---
 kind: ClusterRoleBinding
 apiVersion: rbac.authorization.k8s.io/v1
@@ -421,7 +424,7 @@ spec:
   template:
     metadata:
       annotations:
-        checksum/config: 0d5b035f4d141dc2c13e1f89046de78fe0fb1208075734c3977400b866f2db51
+        checksum/config: 951a7b61ff59229914ca604c671353e5fdc0eda710bde2bfbb50c9225225babe
         linkerd.io/created-by: linkerd/helm dev-undefined
       labels:
         linkerd.io/extension: viz
diff --git a/viz/cmd/testdata/install_prometheus_disabled.golden b/viz/cmd/testdata/install_prometheus_disabled.golden
index 0a8ed9c7d..14d1242b4 100644
--- a/viz/cmd/testdata/install_prometheus_disabled.golden
+++ b/viz/cmd/testdata/install_prometheus_disabled.golden
@@ -39,6 +39,9 @@ rules:
 - apiGroups: ["split.smi-spec.io"]
   resources: ["trafficsplits"]
   verbs: ["list", "get", "watch"]
+- apiGroups: ["policy.linkerd.io"]
+  resources: ["servers", "serverauthorizations"]
+  verbs: ["list", "get"]
 ---
 kind: ClusterRoleBinding
 apiVersion: rbac.authorization.k8s.io/v1
@@ -394,7 +397,7 @@ spec:
   template:
     metadata:
       annotations:
-        checksum/config: 0d5b035f4d141dc2c13e1f89046de78fe0fb1208075734c3977400b866f2db51
+        checksum/config: 951a7b61ff59229914ca604c671353e5fdc0eda710bde2bfbb50c9225225babe
         linkerd.io/created-by: linkerd/helm dev-undefined
       labels:
         linkerd.io/extension: viz
diff --git a/viz/cmd/testdata/install_prometheus_loglevel_from_args.golden b/viz/cmd/testdata/install_prometheus_loglevel_from_args.golden
index 5e78cea2b..63b9c4ed8 100644
--- a/viz/cmd/testdata/install_prometheus_loglevel_from_args.golden
+++ b/viz/cmd/testdata/install_prometheus_loglevel_from_args.golden
@@ -38,6 +38,9 @@ rules:
 - apiGroups: ["split.smi-spec.io"]
   resources: ["trafficsplits"]
   verbs: ["list", "get", "watch"]
+- apiGroups: ["policy.linkerd.io"]
+  resources: ["servers", "serverauthorizations"]
+  verbs: ["list", "get"]
 ---
 kind: ClusterRoleBinding
 apiVersion: rbac.authorization.k8s.io/v1
@@ -434,7 +437,7 @@ spec:
   template:
     metadata:
       annotations:
-        checksum/config: 0d5b035f4d141dc2c13e1f89046de78fe0fb1208075734c3977400b866f2db51
+        checksum/config: 951a7b61ff59229914ca604c671353e5fdc0eda710bde2bfbb50c9225225babe
         linkerd.io/created-by: linkerd/helm dev-undefined
       labels:
         linkerd.io/extension: viz
diff --git a/viz/cmd/testdata/install_proxy_resources.golden b/viz/cmd/testdata/install_proxy_resources.golden
index 443ae637d..2270cf83e 100644
--- a/viz/cmd/testdata/install_proxy_resources.golden
+++ b/viz/cmd/testdata/install_proxy_resources.golden
@@ -38,6 +38,9 @@ rules:
 - apiGroups: ["split.smi-spec.io"]
   resources: ["trafficsplits"]
   verbs: ["list", "get", "watch"]
+- apiGroups: ["policy.linkerd.io"]
+  resources: ["servers", "serverauthorizations"]
+  verbs: ["list", "get"]
 ---
 kind: ClusterRoleBinding
 apiVersion: rbac.authorization.k8s.io/v1
@@ -434,7 +437,7 @@ spec:
   template:
     metadata:
       annotations:
-        checksum/config: 0d5b035f4d141dc2c13e1f89046de78fe0fb1208075734c3977400b866f2db51
+        checksum/config: 951a7b61ff59229914ca604c671353e5fdc0eda710bde2bfbb50c9225225babe
         linkerd.io/created-by: linkerd/helm dev-undefined
       labels:
         linkerd.io/extension: viz