Repository: kumahq/kuma
Pull request #2269 (2021-07-01 19:36:28)

feat(kuma-cp) Add datadog traffic tracing (backport #2247)
This is an automatic backport of pull request #2247 done by [Mergify](https://mergify.io).
Cherry-pick of ba6bebea4b5baadd1dcb699ddec34cd0541b9582 has failed:
```
On branch mergify/bp/release-1.2/pr-2247
Your branch is up to date with 'origin/release-1.2'.

You are currently cherry-picking commit ba6bebea.
  (fix conflicts and run "git cherry-pick --continue")
  (use "git cherry-pick --skip" to skip this patch)
  (use "git cherry-pick --abort" to cancel the cherry-pick operation)

Changes to be committed:
	modified:   DEVELOPER.md
	modified:   api/DEVELOPER.md
	modified:   api/mesh/v1alpha1/known_backends.go
	modified:   api/mesh/v1alpha1/mesh.proto
	modified:   pkg/core/resources/apis/mesh/mesh_validator.go
	modified:   pkg/core/resources/apis/mesh/mesh_validator_test.go
	modified:   pkg/xds/envoy/listeners/configurers.go
	modified:   pkg/xds/envoy/listeners/v3/tracing_configurer.go
	modified:   pkg/xds/envoy/listeners/v3/tracing_configurer_test.go
	modified:   pkg/xds/generator/inbound_proxy_generator.go
	modified:   pkg/xds/generator/outbound_proxy_generator.go
	modified:   pkg/xds/generator/tracing_proxy_generator.go

Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   api/mesh/v1alpha1/mesh.pb.go

```


To fix up this pull request, you can check it out locally. See documentation: https://help.github.com/articles/checking-out-pull-requests-locally/

---


<details>
<summary>Mergify commands and options</summary>

<br />

More conditions and actions can be found in the [documentation](https://docs.mergify.io/).

You can also trigger Mergify actions by commenting on this pull request:

- `@Mergifyio refresh` will re-evaluate the rules
- `@Mergifyio rebase` will rebase this PR on its base branch
- `@Mergifyio update` will merge the base branch into this PR
- `@Mergifyio backport <destination>` will backport this PR on `<destination>` branch

Additionally, on Mergify [dashboard](https://dashboard.mergify.io/) you can:

- look at your merge queues
- generate the Mergify configuration with the config editor.

Finally, you can contact us on https://mergify.io/
</details>


codecov-commenter: # [Codecov](https://codecov.io/gh/kumahq/kuma/pull/2269?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq) Report
> :exclamation: No coverage uploaded for pull request base (`release-1.2@e88ec40`). [Click here to learn what that means](https://docs.codecov.io/docs/error-reference?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq#section-missing-base-commit).
> The diff coverage is `n/a`.

> :exclamation: Current head ba6bebe differs from pull request most recent head c31c159. Consider uploading reports for the commit c31c159 to get more accurate results
[![Impacted file tree graph](https://codecov.io/gh/kumahq/kuma/pull/2269/graphs/tree.svg?width=650&height=150&src=pr&token=D7Yvqs6rwV&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq)](https://codecov.io/gh/kumahq/kuma/pull/2269?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq)

```diff
@@              Coverage Diff               @@
##             release-1.2    #2269   +/-   ##
==============================================
  Coverage               ?   51.60%           
==============================================
  Files                  ?      910           
  Lines                  ?    41201           
  Branches               ?        0           
==============================================
  Hits                   ?    21263           
  Misses                 ?    17900           
  Partials               ?     2038           
```



------

[Continue to review full report at Codecov](https://codecov.io/gh/kumahq/kuma/pull/2269?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/kumahq/kuma/pull/2269?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq). Last update [e88ec40...c31c159](https://codecov.io/gh/kumahq/kuma/pull/2269?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq).

jakubdyszkiewicz: Please do not merge it yet. We need to support Unix socket in order to support APM on Kubernetes.

Patch:
diff --git a/DEVELOPER.md b/DEVELOPER.md
index 53d0dd4f1..f74472b76 100644
--- a/DEVELOPER.md
+++ b/DEVELOPER.md
@@ -50,7 +50,7 @@ docker run --name kuma-build -ti \
   --volume `pwd`:/go/src/github.com/kumahq/kuma \
   --workdir /go/src/github.com/kumahq/kuma \
   --env GO111MODULE=on \
-  golang:1.12.12 \
+  golang:1.16 \
   bash -c 'apt update && apt install -y unzip && export PATH=$HOME/bin:$PATH && bash'
 ```
 
diff --git a/api/DEVELOPER.md b/api/DEVELOPER.md
index 93338dbcc..4ca4195ec 100644
--- a/api/DEVELOPER.md
+++ b/api/DEVELOPER.md
@@ -16,7 +16,7 @@ docker run --rm -ti \
   --workdir /go/src/github.com/kumahq/kuma/api \
   --env HOME=/tmp/home \
   --env GO111MODULE=on \
-  golang:1.12.12 bash
+  golang:1.16 bash
 export PATH=$HOME/bin:$PATH
 apt update && apt install unzip
 ```
@@ -49,4 +49,4 @@ Run:
 
 ```bash
 make build
-```
\ No newline at end of file
+```
diff --git a/api/mesh/v1alpha1/known_backends.go b/api/mesh/v1alpha1/known_backends.go
index cfdc23be8..54cd87ecc 100644
--- a/api/mesh/v1alpha1/known_backends.go
+++ b/api/mesh/v1alpha1/known_backends.go
@@ -4,7 +4,8 @@ const (
 	LoggingTcpType  = "tcp"
 	LoggingFileType = "file"
 
-	TracingZipkinType = "zipkin"
+	TracingZipkinType  = "zipkin"
+	TracingDatadogType = "datadog"
 
 	MetricsPrometheusType = "prometheus"
 )
diff --git a/api/mesh/v1alpha1/mesh.pb.go b/api/mesh/v1alpha1/mesh.pb.go
index 3639e5816..83eb327fc 100644
--- a/api/mesh/v1alpha1/mesh.pb.go
+++ b/api/mesh/v1alpha1/mesh.pb.go
@@ -388,6 +388,63 @@ func (x *TracingBackend) GetConf() *structpb.Struct {
 	return nil
 }
 
+type DatadogTracingBackendConfig struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Address of datadog collector.
+	Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
+	// Port of datadog collector
+	Port uint32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"`
+}
+
+func (x *DatadogTracingBackendConfig) Reset() {
+	*x = DatadogTracingBackendConfig{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[5]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *DatadogTracingBackendConfig) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DatadogTracingBackendConfig) ProtoMessage() {}
+
+func (x *DatadogTracingBackendConfig) ProtoReflect() protoreflect.Message {
+	mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[5]
+	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 DatadogTracingBackendConfig.ProtoReflect.Descriptor instead.
+func (*DatadogTracingBackendConfig) Descriptor() ([]byte, []int) {
+	return file_mesh_v1alpha1_mesh_proto_rawDescGZIP(), []int{5}
+}
+
+func (x *DatadogTracingBackendConfig) GetAddress() string {
+	if x != nil {
+		return x.Address
+	}
+	return ""
+}
+
+func (x *DatadogTracingBackendConfig) GetPort() uint32 {
+	if x != nil {
+		return x.Port
+	}
+	return 0
+}
+
 type ZipkinTracingBackendConfig struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -410,7 +467,7 @@ type ZipkinTracingBackendConfig struct {
 func (x *ZipkinTracingBackendConfig) Reset() {
 	*x = ZipkinTracingBackendConfig{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[5]
+		mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[6]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -423,7 +480,7 @@ func (x *ZipkinTracingBackendConfig) String() string {
 func (*ZipkinTracingBackendConfig) ProtoMessage() {}
 
 func (x *ZipkinTracingBackendConfig) ProtoReflect() protoreflect.Message {
-	mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[5]
+	mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[6]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -436,7 +493,7 @@ func (x *ZipkinTracingBackendConfig) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use ZipkinTracingBackendConfig.ProtoReflect.Descriptor instead.
 func (*ZipkinTracingBackendConfig) Descriptor() ([]byte, []int) {
-	return file_mesh_v1alpha1_mesh_proto_rawDescGZIP(), []int{5}
+	return file_mesh_v1alpha1_mesh_proto_rawDescGZIP(), []int{6}
 }
 
 func (x *ZipkinTracingBackendConfig) GetUrl() string {
@@ -481,7 +538,7 @@ type Logging struct {
 func (x *Logging) Reset() {
 	*x = Logging{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[6]
+		mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[7]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -494,7 +551,7 @@ func (x *Logging) String() string {
 func (*Logging) ProtoMessage() {}
 
 func (x *Logging) ProtoReflect() protoreflect.Message {
-	mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[6]
+	mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[7]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -507,7 +564,7 @@ func (x *Logging) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use Logging.ProtoReflect.Descriptor instead.
 func (*Logging) Descriptor() ([]byte, []int) {
-	return file_mesh_v1alpha1_mesh_proto_rawDescGZIP(), []int{6}
+	return file_mesh_v1alpha1_mesh_proto_rawDescGZIP(), []int{7}
 }
 
 func (x *Logging) GetDefaultBackend() string {
@@ -546,7 +603,7 @@ type LoggingBackend struct {
 func (x *LoggingBackend) Reset() {
 	*x = LoggingBackend{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[7]
+		mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[8]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -559,7 +616,7 @@ func (x *LoggingBackend) String() string {
 func (*LoggingBackend) ProtoMessage() {}
 
 func (x *LoggingBackend) ProtoReflect() protoreflect.Message {
-	mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[7]
+	mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[8]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -572,7 +629,7 @@ func (x *LoggingBackend) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use LoggingBackend.ProtoReflect.Descriptor instead.
 func (*LoggingBackend) Descriptor() ([]byte, []int) {
-	return file_mesh_v1alpha1_mesh_proto_rawDescGZIP(), []int{7}
+	return file_mesh_v1alpha1_mesh_proto_rawDescGZIP(), []int{8}
 }
 
 func (x *LoggingBackend) GetName() string {
@@ -616,7 +673,7 @@ type FileLoggingBackendConfig struct {
 func (x *FileLoggingBackendConfig) Reset() {
 	*x = FileLoggingBackendConfig{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[8]
+		mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[9]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -629,7 +686,7 @@ func (x *FileLoggingBackendConfig) String() string {
 func (*FileLoggingBackendConfig) ProtoMessage() {}
 
 func (x *FileLoggingBackendConfig) ProtoReflect() protoreflect.Message {
-	mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[8]
+	mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[9]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -642,7 +699,7 @@ func (x *FileLoggingBackendConfig) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use FileLoggingBackendConfig.ProtoReflect.Descriptor instead.
 func (*FileLoggingBackendConfig) Descriptor() ([]byte, []int) {
-	return file_mesh_v1alpha1_mesh_proto_rawDescGZIP(), []int{8}
+	return file_mesh_v1alpha1_mesh_proto_rawDescGZIP(), []int{9}
 }
 
 func (x *FileLoggingBackendConfig) GetPath() string {
@@ -665,7 +722,7 @@ type TcpLoggingBackendConfig struct {
 func (x *TcpLoggingBackendConfig) Reset() {
 	*x = TcpLoggingBackendConfig{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[9]
+		mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[10]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -678,7 +735,7 @@ func (x *TcpLoggingBackendConfig) String() string {
 func (*TcpLoggingBackendConfig) ProtoMessage() {}
 
 func (x *TcpLoggingBackendConfig) ProtoReflect() protoreflect.Message {
-	mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[9]
+	mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[10]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -691,7 +748,7 @@ func (x *TcpLoggingBackendConfig) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use TcpLoggingBackendConfig.ProtoReflect.Descriptor instead.
 func (*TcpLoggingBackendConfig) Descriptor() ([]byte, []int) {
-	return file_mesh_v1alpha1_mesh_proto_rawDescGZIP(), []int{9}
+	return file_mesh_v1alpha1_mesh_proto_rawDescGZIP(), []int{10}
 }
 
 func (x *TcpLoggingBackendConfig) GetAddress() string {
@@ -714,7 +771,7 @@ type Routing struct {
 func (x *Routing) Reset() {
 	*x = Routing{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[10]
+		mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[11]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -727,7 +784,7 @@ func (x *Routing) String() string {
 func (*Routing) ProtoMessage() {}
 
 func (x *Routing) ProtoReflect() protoreflect.Message {
-	mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[10]
+	mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[11]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -740,7 +797,7 @@ func (x *Routing) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use Routing.ProtoReflect.Descriptor instead.
 func (*Routing) Descriptor() ([]byte, []int) {
-	return file_mesh_v1alpha1_mesh_proto_rawDescGZIP(), []int{10}
+	return file_mesh_v1alpha1_mesh_proto_rawDescGZIP(), []int{11}
 }
 
 func (x *Routing) GetLocalityAwareLoadBalancing() bool {
@@ -765,7 +822,7 @@ type Mesh_Mtls struct {
 func (x *Mesh_Mtls) Reset() {
 	*x = Mesh_Mtls{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[11]
+		mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[12]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -778,7 +835,7 @@ func (x *Mesh_Mtls) String() string {
 func (*Mesh_Mtls) ProtoMessage() {}
 
 func (x *Mesh_Mtls) ProtoReflect() protoreflect.Message {
-	mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[11]
+	mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[12]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -821,7 +878,7 @@ type CertificateAuthorityBackend_DpCert struct {
 func (x *CertificateAuthorityBackend_DpCert) Reset() {
 	*x = CertificateAuthorityBackend_DpCert{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[12]
+		mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[13]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -834,7 +891,7 @@ func (x *CertificateAuthorityBackend_DpCert) String() string {
 func (*CertificateAuthorityBackend_DpCert) ProtoMessage() {}
 
 func (x *CertificateAuthorityBackend_DpCert) ProtoReflect() protoreflect.Message {
-	mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[12]
+	mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[13]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -870,7 +927,7 @@ type CertificateAuthorityBackend_DpCert_Rotation struct {
 func (x *CertificateAuthorityBackend_DpCert_Rotation) Reset() {
 	*x = CertificateAuthorityBackend_DpCert_Rotation{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[13]
+		mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[14]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -883,7 +940,7 @@ func (x *CertificateAuthorityBackend_DpCert_Rotation) String() string {
 func (*CertificateAuthorityBackend_DpCert_Rotation) ProtoMessage() {}
 
 func (x *CertificateAuthorityBackend_DpCert_Rotation) ProtoReflect() protoreflect.Message {
-	mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[13]
+	mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[14]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -919,7 +976,7 @@ type Networking_Outbound struct {
 func (x *Networking_Outbound) Reset() {
 	*x = Networking_Outbound{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[14]
+		mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[15]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -932,7 +989,7 @@ func (x *Networking_Outbound) String() string {
 func (*Networking_Outbound) ProtoMessage() {}
 
 func (x *Networking_Outbound) ProtoReflect() protoreflect.Message {
-	mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[14]
+	mi := &file_mesh_v1alpha1_mesh_proto_msgTypes[15]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1049,48 +1106,53 @@ var file_mesh_v1alpha1_mesh_proto_rawDesc = []byte{
 	0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x63, 0x6f, 0x6e, 0x66, 0x18,
 	0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
 	0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x04,
-	0x63, 0x6f, 0x6e, 0x66, 0x22, 0xbe, 0x01, 0x0a, 0x1a, 0x5a, 0x69, 0x70, 0x6b, 0x69, 0x6e, 0x54,
+	0x63, 0x6f, 0x6e, 0x66, 0x22, 0x4b, 0x0a, 0x1b, 0x44, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x54,
 	0x72, 0x61, 0x63, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x6e,
-	0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x24, 0x0a, 0x0d, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64,
-	0x31, 0x32, 0x38, 0x62, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x74, 0x72,
-	0x61, 0x63, 0x65, 0x49, 0x64, 0x31, 0x32, 0x38, 0x62, 0x69, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x61,
-	0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x0a, 0x61, 0x70, 0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x48, 0x0a, 0x11, 0x73,
-	0x68, 0x61, 0x72, 0x65, 0x64, 0x53, 0x70, 0x61, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74,
-	0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c,
-	0x75, 0x65, 0x52, 0x11, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x53, 0x70, 0x61, 0x6e, 0x43, 0x6f,
-	0x6e, 0x74, 0x65, 0x78, 0x74, 0x22, 0x71, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67,
-	0x12, 0x26, 0x0a, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x65,
-	0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c,
-	0x74, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x3e, 0x0a, 0x08, 0x62, 0x61, 0x63, 0x6b,
-	0x65, 0x6e, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6b, 0x75, 0x6d,
-	0x61, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
-	0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x08,
-	0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x73, 0x22, 0x7d, 0x0a, 0x0e, 0x4c, 0x6f, 0x67, 0x67,
-	0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
-	0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16,
-	0x0a, 0x06, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
-	0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x63, 0x6f,
-	0x6e, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
-	0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63,
-	0x74, 0x52, 0x04, 0x63, 0x6f, 0x6e, 0x66, 0x22, 0x2e, 0x0a, 0x18, 0x46, 0x69, 0x6c, 0x65, 0x4c,
-	0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x6e,
-	0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x33, 0x0a, 0x17, 0x54, 0x63, 0x70, 0x4c, 0x6f,
-	0x67, 0x67, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66,
-	0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x49, 0x0a, 0x07,
-	0x52, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x3e, 0x0a, 0x1a, 0x6c, 0x6f, 0x63, 0x61, 0x6c,
-	0x69, 0x74, 0x79, 0x41, 0x77, 0x61, 0x72, 0x65, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61,
-	0x6e, 0x63, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x6c, 0x6f, 0x63,
-	0x61, 0x6c, 0x69, 0x74, 0x79, 0x41, 0x77, 0x61, 0x72, 0x65, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61,
-	0x6c, 0x61, 0x6e, 0x63, 0x69, 0x6e, 0x67, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75,
-	0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x75, 0x6d, 0x61, 0x68, 0x71, 0x2f, 0x6b, 0x75, 0x6d,
-	0x61, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70,
-	0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a,
+	0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x70, 0x6f, 0x72,
+	0x74, 0x22, 0xbe, 0x01, 0x0a, 0x1a, 0x5a, 0x69, 0x70, 0x6b, 0x69, 0x6e, 0x54, 0x72, 0x61, 0x63,
+	0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+	0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75,
+	0x72, 0x6c, 0x12, 0x24, 0x0a, 0x0d, 0x74, 0x72, 0x61, 0x63, 0x65, 0x49, 0x64, 0x31, 0x32, 0x38,
+	0x62, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x63, 0x65,
+	0x49, 0x64, 0x31, 0x32, 0x38, 0x62, 0x69, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x70, 0x69, 0x56,
+	0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x70,
+	0x69, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x48, 0x0a, 0x11, 0x73, 0x68, 0x61, 0x72,
+	0x65, 0x64, 0x53, 0x70, 0x61, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52,
+	0x11, 0x73, 0x68, 0x61, 0x72, 0x65, 0x64, 0x53, 0x70, 0x61, 0x6e, 0x43, 0x6f, 0x6e, 0x74, 0x65,
+	0x78, 0x74, 0x22, 0x71, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67, 0x12, 0x26, 0x0a,
+	0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x42, 0x61,
+	0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x3e, 0x0a, 0x08, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64,
+	0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x6d,
+	0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c, 0x6f, 0x67,
+	0x67, 0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x52, 0x08, 0x62, 0x61, 0x63,
+	0x6b, 0x65, 0x6e, 0x64, 0x73, 0x22, 0x7d, 0x0a, 0x0e, 0x4c, 0x6f, 0x67, 0x67, 0x69, 0x6e, 0x67,
+	0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x66,
+	0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x66, 0x6f, 0x72,
+	0x6d, 0x61, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x63, 0x6f, 0x6e, 0x66, 0x18,
+	0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x04,
+	0x63, 0x6f, 0x6e, 0x66, 0x22, 0x2e, 0x0a, 0x18, 0x46, 0x69, 0x6c, 0x65, 0x4c, 0x6f, 0x67, 0x67,
+	0x69, 0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+	0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
+	0x70, 0x61, 0x74, 0x68, 0x22, 0x33, 0x0a, 0x17, 0x54, 0x63, 0x70, 0x4c, 0x6f, 0x67, 0x67, 0x69,
+	0x6e, 0x67, 0x42, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
+	0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x49, 0x0a, 0x07, 0x52, 0x6f, 0x75,
+	0x74, 0x69, 0x6e, 0x67, 0x12, 0x3e, 0x0a, 0x1a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x74, 0x79,
+	0x41, 0x77, 0x61, 0x72, 0x65, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x69,
+	0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x69,
+	0x74, 0x79, 0x41, 0x77, 0x61, 0x72, 0x65, 0x4c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x6c, 0x61, 0x6e,
+	0x63, 0x69, 0x6e, 0x67, 0x42, 0x2a, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
+	0x6f, 0x6d, 0x2f, 0x6b, 0x75, 0x6d, 0x61, 0x68, 0x71, 0x2f, 0x6b, 0x75, 0x6d, 0x61, 0x2f, 0x61,
+	0x70, 0x69, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
+	0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -1105,47 +1167,48 @@ func file_mesh_v1alpha1_mesh_proto_rawDescGZIP() []byte {
 	return file_mesh_v1alpha1_mesh_proto_rawDescData
 }
 
-var file_mesh_v1alpha1_mesh_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
+var file_mesh_v1alpha1_mesh_proto_msgTypes = make([]protoimpl.MessageInfo, 16)
 var file_mesh_v1alpha1_mesh_proto_goTypes = []interface{}{
 	(*Mesh)(nil),                                        // 0: kuma.mesh.v1alpha1.Mesh
 	(*CertificateAuthorityBackend)(nil),                 // 1: kuma.mesh.v1alpha1.CertificateAuthorityBackend
 	(*Networking)(nil),                                  // 2: kuma.mesh.v1alpha1.Networking
 	(*Tracing)(nil),                                     // 3: kuma.mesh.v1alpha1.Tracing
 	(*TracingBackend)(nil),                              // 4: kuma.mesh.v1alpha1.TracingBackend
-	(*ZipkinTracingBackendConfig)(nil),                  // 5: kuma.mesh.v1alpha1.ZipkinTracingBackendConfig
-	(*Logging)(nil),                                     // 6: kuma.mesh.v1alpha1.Logging
-	(*LoggingBackend)(nil),                              // 7: kuma.mesh.v1alpha1.LoggingBackend
-	(*FileLoggingBackendConfig)(nil),                    // 8: kuma.mesh.v1alpha1.FileLoggingBackendConfig
-	(*TcpLoggingBackendConfig)(nil),                     // 9: kuma.mesh.v1alpha1.TcpLoggingBackendConfig
-	(*Routing)(nil),                                     // 10: kuma.mesh.v1alpha1.Routing
-	(*Mesh_Mtls)(nil),                                   // 11: kuma.mesh.v1alpha1.Mesh.Mtls
-	(*CertificateAuthorityBackend_DpCert)(nil),          // 12: kuma.mesh.v1alpha1.CertificateAuthorityBackend.DpCert
-	(*CertificateAuthorityBackend_DpCert_Rotation)(nil), // 13: kuma.mesh.v1alpha1.CertificateAuthorityBackend.DpCert.Rotation
-	(*Networking_Outbound)(nil),                         // 14: kuma.mesh.v1alpha1.Networking.Outbound
-	(*Metrics)(nil),                                     // 15: kuma.mesh.v1alpha1.Metrics
-	(*structpb.Struct)(nil),                             // 16: google.protobuf.Struct
-	(*wrapperspb.DoubleValue)(nil),                      // 17: google.protobuf.DoubleValue
-	(*wrapperspb.BoolValue)(nil),                        // 18: google.protobuf.BoolValue
+	(*DatadogTracingBackendConfig)(nil),                 // 5: kuma.mesh.v1alpha1.DatadogTracingBackendConfig
+	(*ZipkinTracingBackendConfig)(nil),                  // 6: kuma.mesh.v1alpha1.ZipkinTracingBackendConfig
+	(*Logging)(nil),                                     // 7: kuma.mesh.v1alpha1.Logging
+	(*LoggingBackend)(nil),                              // 8: kuma.mesh.v1alpha1.LoggingBackend
+	(*FileLoggingBackendConfig)(nil),                    // 9: kuma.mesh.v1alpha1.FileLoggingBackendConfig
+	(*TcpLoggingBackendConfig)(nil),                     // 10: kuma.mesh.v1alpha1.TcpLoggingBackendConfig
+	(*Routing)(nil),                                     // 11: kuma.mesh.v1alpha1.Routing
+	(*Mesh_Mtls)(nil),                                   // 12: kuma.mesh.v1alpha1.Mesh.Mtls
+	(*CertificateAuthorityBackend_DpCert)(nil),          // 13: kuma.mesh.v1alpha1.CertificateAuthorityBackend.DpCert
+	(*CertificateAuthorityBackend_DpCert_Rotation)(nil), // 14: kuma.mesh.v1alpha1.CertificateAuthorityBackend.DpCert.Rotation
+	(*Networking_Outbound)(nil),                         // 15: kuma.mesh.v1alpha1.Networking.Outbound
+	(*Metrics)(nil),                                     // 16: kuma.mesh.v1alpha1.Metrics
+	(*structpb.Struct)(nil),                             // 17: google.protobuf.Struct
+	(*wrapperspb.DoubleValue)(nil),                      // 18: google.protobuf.DoubleValue
+	(*wrapperspb.BoolValue)(nil),                        // 19: google.protobuf.BoolValue
 }
 var file_mesh_v1alpha1_mesh_proto_depIdxs = []int32{
-	11, // 0: kuma.mesh.v1alpha1.Mesh.mtls:type_name -> kuma.mesh.v1alpha1.Mesh.Mtls
+	12, // 0: kuma.mesh.v1alpha1.Mesh.mtls:type_name -> kuma.mesh.v1alpha1.Mesh.Mtls
 	3,  // 1: kuma.mesh.v1alpha1.Mesh.tracing:type_name -> kuma.mesh.v1alpha1.Tracing
-	6,  // 2: kuma.mesh.v1alpha1.Mesh.logging:type_name -> kuma.mesh.v1alpha1.Logging
-	15, // 3: kuma.mesh.v1alpha1.Mesh.metrics:type_name -> kuma.mesh.v1alpha1.Metrics
+	7,  // 2: kuma.mesh.v1alpha1.Mesh.logging:type_name -> kuma.mesh.v1alpha1.Logging
+	16, // 3: kuma.mesh.v1alpha1.Mesh.metrics:type_name -> kuma.mesh.v1alpha1.Metrics
 	2,  // 4: kuma.mesh.v1alpha1.Mesh.networking:type_name -> kuma.mesh.v1alpha1.Networking
-	10, // 5: kuma.mesh.v1alpha1.Mesh.routing:type_name -> kuma.mesh.v1alpha1.Routing
-	12, // 6: kuma.mesh.v1alpha1.CertificateAuthorityBackend.dpCert:type_name -> kuma.mesh.v1alpha1.CertificateAuthorityBackend.DpCert
-	16, // 7: kuma.mesh.v1alpha1.CertificateAuthorityBackend.conf:type_name -> google.protobuf.Struct
-	14, // 8: kuma.mesh.v1alpha1.Networking.outbound:type_name -> kuma.mesh.v1alpha1.Networking.Outbound
+	11, // 5: kuma.mesh.v1alpha1.Mesh.routing:type_name -> kuma.mesh.v1alpha1.Routing
+	13, // 6: kuma.mesh.v1alpha1.CertificateAuthorityBackend.dpCert:type_name -> kuma.mesh.v1alpha1.CertificateAuthorityBackend.DpCert
+	17, // 7: kuma.mesh.v1alpha1.CertificateAuthorityBackend.conf:type_name -> google.protobuf.Struct
+	15, // 8: kuma.mesh.v1alpha1.Networking.outbound:type_name -> kuma.mesh.v1alpha1.Networking.Outbound
 	4,  // 9: kuma.mesh.v1alpha1.Tracing.backends:type_name -> kuma.mesh.v1alpha1.TracingBackend
-	17, // 10: kuma.mesh.v1alpha1.TracingBackend.sampling:type_name -> google.protobuf.DoubleValue
-	16, // 11: kuma.mesh.v1alpha1.TracingBackend.conf:type_name -> google.protobuf.Struct
-	18, // 12: kuma.mesh.v1alpha1.ZipkinTracingBackendConfig.sharedSpanContext:type_name -> google.protobuf.BoolValue
-	7,  // 13: kuma.mesh.v1alpha1.Logging.backends:type_name -> kuma.mesh.v1alpha1.LoggingBackend
-	16, // 14: kuma.mesh.v1alpha1.LoggingBackend.conf:type_name -> google.protobuf.Struct
+	18, // 10: kuma.mesh.v1alpha1.TracingBackend.sampling:type_name -> google.protobuf.DoubleValue
+	17, // 11: kuma.mesh.v1alpha1.TracingBackend.conf:type_name -> google.protobuf.Struct
+	19, // 12: kuma.mesh.v1alpha1.ZipkinTracingBackendConfig.sharedSpanContext:type_name -> google.protobuf.BoolValue
+	8,  // 13: kuma.mesh.v1alpha1.Logging.backends:type_name -> kuma.mesh.v1alpha1.LoggingBackend
+	17, // 14: kuma.mesh.v1alpha1.LoggingBackend.conf:type_name -> google.protobuf.Struct
 	1,  // 15: kuma.mesh.v1alpha1.Mesh.Mtls.backends:type_name -> kuma.mesh.v1alpha1.CertificateAuthorityBackend
-	13, // 16: kuma.mesh.v1alpha1.CertificateAuthorityBackend.DpCert.rotation:type_name -> kuma.mesh.v1alpha1.CertificateAuthorityBackend.DpCert.Rotation
-	18, // 17: kuma.mesh.v1alpha1.Networking.Outbound.passthrough:type_name -> google.protobuf.BoolValue
+	14, // 16: kuma.mesh.v1alpha1.CertificateAuthorityBackend.DpCert.rotation:type_name -> kuma.mesh.v1alpha1.CertificateAuthorityBackend.DpCert.Rotation
+	19, // 17: kuma.mesh.v1alpha1.Networking.Outbound.passthrough:type_name -> google.protobuf.BoolValue
 	18, // [18:18] is the sub-list for method output_type
 	18, // [18:18] is the sub-list for method input_type
 	18, // [18:18] is the sub-list for extension type_name
@@ -1221,7 +1284,7 @@ func file_mesh_v1alpha1_mesh_proto_init() {
 			}
 		}
 		file_mesh_v1alpha1_mesh_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*ZipkinTracingBackendConfig); i {
+			switch v := v.(*DatadogTracingBackendConfig); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -1233,7 +1296,7 @@ func file_mesh_v1alpha1_mesh_proto_init() {
 			}
 		}
 		file_mesh_v1alpha1_mesh_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*Logging); i {
+			switch v := v.(*ZipkinTracingBackendConfig); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -1245,7 +1308,7 @@ func file_mesh_v1alpha1_mesh_proto_init() {
 			}
 		}
 		file_mesh_v1alpha1_mesh_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*LoggingBackend); i {
+			switch v := v.(*Logging); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -1257,7 +1320,7 @@ func file_mesh_v1alpha1_mesh_proto_init() {
 			}
 		}
 		file_mesh_v1alpha1_mesh_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*FileLoggingBackendConfig); i {
+			switch v := v.(*LoggingBackend); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -1269,7 +1332,7 @@ func file_mesh_v1alpha1_mesh_proto_init() {
 			}
 		}
 		file_mesh_v1alpha1_mesh_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*TcpLoggingBackendConfig); i {
+			switch v := v.(*FileLoggingBackendConfig); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -1281,7 +1344,7 @@ func file_mesh_v1alpha1_mesh_proto_init() {
 			}
 		}
 		file_mesh_v1alpha1_mesh_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*Routing); i {
+			switch v := v.(*TcpLoggingBackendConfig); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -1293,7 +1356,7 @@ func file_mesh_v1alpha1_mesh_proto_init() {
 			}
 		}
 		file_mesh_v1alpha1_mesh_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*Mesh_Mtls); i {
+			switch v := v.(*Routing); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -1305,7 +1368,7 @@ func file_mesh_v1alpha1_mesh_proto_init() {
 			}
 		}
 		file_mesh_v1alpha1_mesh_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*CertificateAuthorityBackend_DpCert); i {
+			switch v := v.(*Mesh_Mtls); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -1317,7 +1380,7 @@ func file_mesh_v1alpha1_mesh_proto_init() {
 			}
 		}
 		file_mesh_v1alpha1_mesh_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*CertificateAuthorityBackend_DpCert_Rotation); i {
+			switch v := v.(*CertificateAuthorityBackend_DpCert); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -1329,6 +1392,18 @@ func file_mesh_v1alpha1_mesh_proto_init() {
 			}
 		}
 		file_mesh_v1alpha1_mesh_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*CertificateAuthorityBackend_DpCert_Rotation); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_mesh_v1alpha1_mesh_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*Networking_Outbound); i {
 			case 0:
 				return &v.state
@@ -1347,7 +1422,7 @@ func file_mesh_v1alpha1_mesh_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_mesh_v1alpha1_mesh_proto_rawDesc,
 			NumEnums:      0,
-			NumMessages:   15,
+			NumMessages:   16,
 			NumExtensions: 0,
 			NumServices:   0,
 		},
diff --git a/api/mesh/v1alpha1/mesh.proto b/api/mesh/v1alpha1/mesh.proto
index 90c16ecb1..6c6c7ef13 100644
--- a/api/mesh/v1alpha1/mesh.proto
+++ b/api/mesh/v1alpha1/mesh.proto
@@ -123,6 +123,14 @@ message TracingBackend {
   google.protobuf.Struct conf = 4;
 }
 
+message DatadogTracingBackendConfig {
+  // Address of datadog collector.
+  string address = 1;
+
+  // Port of datadog collector
+  uint32 port = 2;
+}
+
 message ZipkinTracingBackendConfig {
   // Address of Zipkin collector.
   string url = 1;
diff --git a/api/system/v1alpha1/config.pb.go b/api/system/v1alpha1/config.pb.go
index a748a4710..56e0c69f3 100644
--- a/api/system/v1alpha1/config.pb.go
+++ b/api/system/v1alpha1/config.pb.go
@@ -7,6 +7,7 @@
 package v1alpha1
 
 import (
+	_ "github.com/kumahq/kuma/api/mesh"
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
 	reflect "reflect"
@@ -76,12 +77,18 @@ var file_system_v1alpha1_config_proto_rawDesc = []byte{
 	0x0a, 0x1c, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
 	0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x14,
 	0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x61, 0x6c,
-	0x70, 0x68, 0x61, 0x31, 0x22, 0x20, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x16,
-	0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
-	0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
-	0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x75, 0x6d, 0x61, 0x68, 0x71, 0x2f, 0x6b, 0x75, 0x6d, 0x61,
-	0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c,
-	0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x70, 0x68, 0x61, 0x31, 0x1a, 0x12, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f,
+	0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x64, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66,
+	0x69, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x3a, 0x42, 0xaa, 0x8c, 0x89, 0xa6,
+	0x01, 0x10, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
+	0x63, 0x65, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x08, 0x12, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+	0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x08, 0x22, 0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0xaa, 0x8c,
+	0x89, 0xa6, 0x01, 0x02, 0x18, 0x01, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x02, 0x28, 0x01, 0x42, 0x2c,
+	0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x75, 0x6d,
+	0x61, 0x68, 0x71, 0x2f, 0x6b, 0x75, 0x6d, 0x61, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x79, 0x73,
+	0x74, 0x65, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
diff --git a/api/system/v1alpha1/config.proto b/api/system/v1alpha1/config.proto
index b92e80f01..552b201b9 100644
--- a/api/system/v1alpha1/config.proto
+++ b/api/system/v1alpha1/config.proto
@@ -4,9 +4,18 @@ package kuma.system.v1alpha1;
 
 option go_package = "github.com/kumahq/kuma/api/system/v1alpha1";
 
+import "mesh/options.proto";
+
 // Config is a entity that represents dynamic configuration that is stored in
 // underlying storage. For now it's used only for internal mechanisms.
 message Config {
+
+  option (kuma.mesh.resource).name = "ConfigResource";
+  option (kuma.mesh.resource).type = "Config";
+  option (kuma.mesh.resource).package = "system";
+  option (kuma.mesh.resource).global = true;
+  option (kuma.mesh.resource).skip_validation = true;
+
   // configuration that is stored (ex. in JSON)
   string config = 1;
 }
diff --git a/api/system/v1alpha1/secret.pb.go b/api/system/v1alpha1/secret.pb.go
index 7b18f4bb0..74859ea8b 100644
--- a/api/system/v1alpha1/secret.pb.go
+++ b/api/system/v1alpha1/secret.pb.go
@@ -7,6 +7,7 @@
 package v1alpha1
 
 import (
+	_ "github.com/kumahq/kuma/api/mesh"
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
 	wrapperspb "google.golang.org/protobuf/types/known/wrapperspb"
@@ -21,7 +22,7 @@ const (
 	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
 )
 
-// Secret defines encrypted value in Kuma
+// Secret defines an encrypted value in Kuma.
 type Secret struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -76,12 +77,17 @@ var file_system_v1alpha1_secret_proto_rawDesc = []byte{
 	0x0a, 0x1c, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
 	0x31, 0x2f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x14,
 	0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x61, 0x6c,
-	0x70, 0x68, 0x61, 0x31, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f,
-	0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70,
-	0x72, 0x6f, 0x74, 0x6f, 0x22, 0x39, 0x0a, 0x06, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x2f,
-	0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67,
-	0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42,
-	0x79, 0x74, 0x65, 0x73, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x42,
+	0x70, 0x68, 0x61, 0x31, 0x1a, 0x12, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f,
+	0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
+	0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65,
+	0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x75, 0x0a, 0x06, 0x53, 0x65, 0x63, 0x72,
+	0x65, 0x74, 0x12, 0x2f, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
+	0x75, 0x66, 0x2e, 0x42, 0x79, 0x74, 0x65, 0x73, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x04, 0x64,
+	0x61, 0x74, 0x61, 0x3a, 0x3a, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x10, 0x0a, 0x0e, 0x53, 0x65, 0x63,
+	0x72, 0x65, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0xaa, 0x8c, 0x89, 0xa6, 0x01,
+	0x08, 0x12, 0x06, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x08, 0x22,
+	0x06, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x02, 0x28, 0x01, 0x42,
 	0x2c, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x75,
 	0x6d, 0x61, 0x68, 0x71, 0x2f, 0x6b, 0x75, 0x6d, 0x61, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x79,
 	0x73, 0x74, 0x65, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70,
diff --git a/api/system/v1alpha1/secret.proto b/api/system/v1alpha1/secret.proto
index 9dea72612..414b2c0e6 100644
--- a/api/system/v1alpha1/secret.proto
+++ b/api/system/v1alpha1/secret.proto
@@ -4,10 +4,17 @@ package kuma.system.v1alpha1;
 
 option go_package = "github.com/kumahq/kuma/api/system/v1alpha1";
 
+import "mesh/options.proto";
 import "google/protobuf/wrappers.proto";
 
-// Secret defines encrypted value in Kuma
+// Secret defines an encrypted value in Kuma.
 message Secret {
+
+  option (kuma.mesh.resource).name = "SecretResource";
+  option (kuma.mesh.resource).type = "Secret";
+  option (kuma.mesh.resource).package = "system";
+  option (kuma.mesh.resource).skip_validation = true;
+
   // Value of the secret
   google.protobuf.BytesValue data = 1;
 }
diff --git a/api/system/v1alpha1/zone.pb.go b/api/system/v1alpha1/zone.pb.go
index 999402dfa..0864f2672 100644
--- a/api/system/v1alpha1/zone.pb.go
+++ b/api/system/v1alpha1/zone.pb.go
@@ -7,6 +7,7 @@
 package v1alpha1
 
 import (
+	_ "github.com/kumahq/kuma/api/mesh"
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
 	wrapperspb "google.golang.org/protobuf/types/known/wrapperspb"
@@ -78,16 +79,21 @@ var file_system_v1alpha1_zone_proto_rawDesc = []byte{
 	0x0a, 0x1a, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
 	0x31, 0x2f, 0x7a, 0x6f, 0x6e, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x14, 0x6b, 0x75,
 	0x6d, 0x61, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
-	0x61, 0x31, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-	0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f,
-	0x74, 0x6f, 0x22, 0x3c, 0x0a, 0x04, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x34, 0x0a, 0x07, 0x65, 0x6e,
-	0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f,
-	0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f,
-	0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64,
-	0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b,
-	0x75, 0x6d, 0x61, 0x68, 0x71, 0x2f, 0x6b, 0x75, 0x6d, 0x61, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73,
-	0x79, 0x73, 0x74, 0x65, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x61, 0x31, 0x1a, 0x12, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73,
+	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x74, 0x0a, 0x04, 0x5a, 0x6f, 0x6e, 0x65, 0x12, 0x34,
+	0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
+	0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x65, 0x6e, 0x61,
+	0x62, 0x6c, 0x65, 0x64, 0x3a, 0x36, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x0e, 0x0a, 0x0c, 0x5a, 0x6f,
+	0x6e, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x06,
+	0x12, 0x04, 0x5a, 0x6f, 0x6e, 0x65, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x08, 0x22, 0x06, 0x73, 0x79,
+	0x73, 0x74, 0x65, 0x6d, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x02, 0x18, 0x01, 0x42, 0x2c, 0x5a, 0x2a,
+	0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x75, 0x6d, 0x61, 0x68,
+	0x71, 0x2f, 0x6b, 0x75, 0x6d, 0x61, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x79, 0x73, 0x74, 0x65,
+	0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x33,
 }
 
 var (
diff --git a/api/system/v1alpha1/zone.proto b/api/system/v1alpha1/zone.proto
index c78af8855..3a6d2a520 100644
--- a/api/system/v1alpha1/zone.proto
+++ b/api/system/v1alpha1/zone.proto
@@ -4,11 +4,18 @@ package kuma.system.v1alpha1;
 
 option go_package = "github.com/kumahq/kuma/api/system/v1alpha1";
 
+import "mesh/options.proto";
 import "google/protobuf/wrappers.proto";
 
 // Zone defines the Zone configuration used at the Global Control Plane
 // within a distributed deployment
 message Zone {
+
+  option (kuma.mesh.resource).name = "ZoneResource";
+  option (kuma.mesh.resource).type = "Zone";
+  option (kuma.mesh.resource).package = "system";
+  option (kuma.mesh.resource).global = true;
+
   // enable allows to turn the zone on/off and exclude the whole zone from
   // balancing traffic on it
   google.protobuf.BoolValue enabled = 1;
diff --git a/api/system/v1alpha1/zone_insight.pb.go b/api/system/v1alpha1/zone_insight.pb.go
index 393893c1b..6629c18e3 100644
--- a/api/system/v1alpha1/zone_insight.pb.go
+++ b/api/system/v1alpha1/zone_insight.pb.go
@@ -8,6 +8,7 @@ package v1alpha1
 
 import (
 	_ "github.com/envoyproxy/protoc-gen-validate/validate"
+	_ "github.com/kumahq/kuma/api/mesh"
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
 	timestamppb "google.golang.org/protobuf/types/known/timestamppb"
@@ -438,86 +439,93 @@ var file_system_v1alpha1_zone_insight_proto_rawDesc = []byte{
 	0x0a, 0x22, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
 	0x31, 0x2f, 0x7a, 0x6f, 0x6e, 0x65, 0x5f, 0x69, 0x6e, 0x73, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x70,
 	0x72, 0x6f, 0x74, 0x6f, 0x12, 0x14, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65,
-	0x6d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67,
-	0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65,
-	0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x76, 0x61, 0x6c,
-	0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70,
-	0x72, 0x6f, 0x74, 0x6f, 0x22, 0x5a, 0x0a, 0x0b, 0x5a, 0x6f, 0x6e, 0x65, 0x49, 0x6e, 0x73, 0x69,
-	0x67, 0x68, 0x74, 0x12, 0x4b, 0x0a, 0x0d, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
-	0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6b, 0x75, 0x6d,
-	0x61, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
-	0x31, 0x2e, 0x4b, 0x44, 0x53, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
-	0x6e, 0x52, 0x0d, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73,
-	0x22, 0xf7, 0x02, 0x0a, 0x0f, 0x4b, 0x44, 0x53, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70,
-	0x74, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
-	0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x35, 0x0a,
-	0x12, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65,
-	0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02,
-	0x10, 0x01, 0x52, 0x10, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e,
-	0x63, 0x65, 0x49, 0x64, 0x12, 0x47, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x5f,
-	0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
-	0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d,
-	0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x08, 0xfa, 0x42, 0x05, 0xb2, 0x01, 0x02, 0x08, 0x01,
-	0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x43, 0x0a,
-	0x0f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65,
-	0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
-	0x6d, 0x70, 0x52, 0x0e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x54, 0x69,
-	0x6d, 0x65, 0x12, 0x4d, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, 0x01,
-	0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d,
-	0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4b, 0x44, 0x53, 0x53, 0x75, 0x62,
-	0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42,
-	0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75,
-	0x73, 0x12, 0x37, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01,
-	0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d,
-	0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
-	0x6e, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xc5, 0x02, 0x0a, 0x15, 0x4b,
-	0x44, 0x53, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74,
-	0x61, 0x74, 0x75, 0x73, 0x12, 0x44, 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x70, 0x64,
-	0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a,
-	0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
-	0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x6c, 0x61, 0x73, 0x74,
-	0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x3b, 0x0a, 0x05, 0x74, 0x6f,
-	0x74, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6b, 0x75, 0x6d, 0x61,
-	0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
-	0x2e, 0x4b, 0x44, 0x53, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73,
-	0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x49, 0x0a, 0x04, 0x73, 0x74, 0x61, 0x74, 0x18,
-	0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x73, 0x79, 0x73,
+	0x6d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x1a, 0x12, 0x6d, 0x65, 0x73, 0x68,
+	0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f,
+	0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f,
+	0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
+	0x17, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61,
+	0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa8, 0x01, 0x0a, 0x0b, 0x5a, 0x6f, 0x6e,
+	0x65, 0x49, 0x6e, 0x73, 0x69, 0x67, 0x68, 0x74, 0x12, 0x4b, 0x0a, 0x0d, 0x73, 0x75, 0x62, 0x73,
+	0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
+	0x25, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31,
+	0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4b, 0x44, 0x53, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72,
+	0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70,
+	0x74, 0x69, 0x6f, 0x6e, 0x73, 0x3a, 0x4c, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x15, 0x0a, 0x13, 0x5a,
+	0x6f, 0x6e, 0x65, 0x49, 0x6e, 0x73, 0x69, 0x67, 0x68, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
+	0x63, 0x65, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x0d, 0x12, 0x0b, 0x5a, 0x6f, 0x6e, 0x65, 0x49, 0x6e,
+	0x73, 0x69, 0x67, 0x68, 0x74, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x08, 0x22, 0x06, 0x73, 0x79, 0x73,
+	0x74, 0x65, 0x6d, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x02, 0x18, 0x01, 0xaa, 0x8c, 0x89, 0xa6, 0x01,
+	0x02, 0x28, 0x01, 0x22, 0xf7, 0x02, 0x0a, 0x0f, 0x4b, 0x44, 0x53, 0x53, 0x75, 0x62, 0x73, 0x63,
+	0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x02, 0x69, 0x64,
+	0x12, 0x35, 0x0a, 0x12, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x69, 0x6e, 0x73, 0x74, 0x61,
+	0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42,
+	0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x10, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x49, 0x6e, 0x73,
+	0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x47, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x6e, 0x65,
+	0x63, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e,
+	0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
+	0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x08, 0xfa, 0x42, 0x05, 0xb2, 0x01,
+	0x02, 0x08, 0x01, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x54, 0x69, 0x6d, 0x65,
+	0x12, 0x43, 0x0a, 0x0f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x5f, 0x74,
+	0x69, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
+	0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65,
+	0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
+	0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x4d, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18,
+	0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x73, 0x79, 0x73,
 	0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4b, 0x44, 0x53,
 	0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74,
-	0x75, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x73, 0x74,
-	0x61, 0x74, 0x1a, 0x5e, 0x0a, 0x09, 0x53, 0x74, 0x61, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,
-	0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65,
-	0x79, 0x12, 0x3b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
-	0x32, 0x25, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x76,
-	0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4b, 0x44, 0x53, 0x53, 0x65, 0x72, 0x76, 0x69,
-	0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
-	0x38, 0x01, 0x22, 0x9e, 0x01, 0x0a, 0x0f, 0x4b, 0x44, 0x53, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
-	0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e,
-	0x73, 0x65, 0x73, 0x5f, 0x73, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d,
-	0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x53, 0x65, 0x6e, 0x74, 0x12, 0x35, 0x0a,
-	0x16, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x5f, 0x61, 0x63, 0x6b, 0x6e, 0x6f,
-	0x77, 0x6c, 0x65, 0x64, 0x67, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x15, 0x72,
-	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x41, 0x63, 0x6b, 0x6e, 0x6f, 0x77, 0x6c, 0x65,
-	0x64, 0x67, 0x65, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
-	0x73, 0x5f, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04,
-	0x52, 0x11, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x52, 0x65, 0x6a, 0x65, 0x63,
-	0x74, 0x65, 0x64, 0x22, 0x46, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3b,
-	0x0a, 0x06, 0x6b, 0x75, 0x6d, 0x61, 0x43, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23,
-	0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x61,
-	0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4b, 0x75, 0x6d, 0x61, 0x43, 0x70, 0x56, 0x65, 0x72, 0x73,
-	0x69, 0x6f, 0x6e, 0x52, 0x06, 0x6b, 0x75, 0x6d, 0x61, 0x43, 0x70, 0x22, 0x7d, 0x0a, 0x0d, 0x4b,
-	0x75, 0x6d, 0x61, 0x43, 0x70, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07,
-	0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76,
-	0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x69, 0x74, 0x54, 0x61, 0x67,
-	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x67, 0x69, 0x74, 0x54, 0x61, 0x67, 0x12, 0x1c,
-	0x0a, 0x09, 0x67, 0x69, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x09, 0x67, 0x69, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x12, 0x1c, 0x0a, 0x09,
-	0x62, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x09, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x61, 0x74, 0x65, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69,
-	0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x75, 0x6d, 0x61, 0x68, 0x71, 0x2f,
-	0x6b, 0x75, 0x6d, 0x61, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2f,
-	0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x75, 0x73, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x06, 0x73, 0x74,
+	0x61, 0x74, 0x75, 0x73, 0x12, 0x37, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18,
+	0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x73, 0x79, 0x73,
+	0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x56, 0x65, 0x72,
+	0x73, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xc5, 0x02,
+	0x0a, 0x15, 0x4b, 0x44, 0x53, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
+	0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x44, 0x0a, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x5f,
+	0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x6c,
+	0x61, 0x73, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x3b, 0x0a,
+	0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6b,
+	0x75, 0x6d, 0x61, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
+	0x68, 0x61, 0x31, 0x2e, 0x4b, 0x44, 0x53, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74,
+	0x61, 0x74, 0x73, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x49, 0x0a, 0x04, 0x73, 0x74,
+	0x61, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e,
+	0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
+	0x4b, 0x44, 0x53, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x53,
+	0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52,
+	0x04, 0x73, 0x74, 0x61, 0x74, 0x1a, 0x5e, 0x0a, 0x09, 0x53, 0x74, 0x61, 0x74, 0x45, 0x6e, 0x74,
+	0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x03, 0x6b, 0x65, 0x79, 0x12, 0x3b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65,
+	0x6d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4b, 0x44, 0x53, 0x53, 0x65,
+	0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
+	0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x9e, 0x01, 0x0a, 0x0f, 0x4b, 0x44, 0x53, 0x53, 0x65, 0x72,
+	0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x72, 0x65, 0x73,
+	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x5f, 0x73, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x04, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x53, 0x65, 0x6e, 0x74,
+	0x12, 0x35, 0x0a, 0x16, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x5f, 0x61, 0x63,
+	0x6b, 0x6e, 0x6f, 0x77, 0x6c, 0x65, 0x64, 0x67, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04,
+	0x52, 0x15, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x41, 0x63, 0x6b, 0x6e, 0x6f,
+	0x77, 0x6c, 0x65, 0x64, 0x67, 0x65, 0x64, 0x12, 0x2d, 0x0a, 0x12, 0x72, 0x65, 0x73, 0x70, 0x6f,
+	0x6e, 0x73, 0x65, 0x73, 0x5f, 0x72, 0x65, 0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20,
+	0x01, 0x28, 0x04, 0x52, 0x11, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x52, 0x65,
+	0x6a, 0x65, 0x63, 0x74, 0x65, 0x64, 0x22, 0x46, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
+	0x6e, 0x12, 0x3b, 0x0a, 0x06, 0x6b, 0x75, 0x6d, 0x61, 0x43, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x0b, 0x32, 0x23, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e,
+	0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4b, 0x75, 0x6d, 0x61, 0x43, 0x70, 0x56,
+	0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x06, 0x6b, 0x75, 0x6d, 0x61, 0x43, 0x70, 0x22, 0x7d,
+	0x0a, 0x0d, 0x4b, 0x75, 0x6d, 0x61, 0x43, 0x70, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12,
+	0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x69, 0x74,
+	0x54, 0x61, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x67, 0x69, 0x74, 0x54, 0x61,
+	0x67, 0x12, 0x1c, 0x0a, 0x09, 0x67, 0x69, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x18, 0x03,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x69, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x12,
+	0x1c, 0x0a, 0x09, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x09, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x61, 0x74, 0x65, 0x42, 0x2c, 0x5a,
+	0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x75, 0x6d, 0x61,
+	0x68, 0x71, 0x2f, 0x6b, 0x75, 0x6d, 0x61, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x79, 0x73, 0x74,
+	0x65, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x33,
 }
 
 var (
diff --git a/api/system/v1alpha1/zone_insight.proto b/api/system/v1alpha1/zone_insight.proto
index 07a634697..4b65690dd 100644
--- a/api/system/v1alpha1/zone_insight.proto
+++ b/api/system/v1alpha1/zone_insight.proto
@@ -4,12 +4,19 @@ package kuma.system.v1alpha1;
 
 option go_package = "github.com/kumahq/kuma/api/system/v1alpha1";
 
+import "mesh/options.proto";
 import "google/protobuf/timestamp.proto";
 import "validate/validate.proto";
 
 // ZoneInsight defines the observed state of a Zone Kuma CP.
 message ZoneInsight {
 
+  option (kuma.mesh.resource).name = "ZoneInsightResource";
+  option (kuma.mesh.resource).type = "ZoneInsight";
+  option (kuma.mesh.resource).package = "system";
+  option (kuma.mesh.resource).global = true;
+  option (kuma.mesh.resource).skip_validation = true;
+
   // List of KDS subscriptions created by a given Zone Kuma CP.
   repeated KDSSubscription subscriptions = 1;
 }
diff --git a/api/system/v1alpha1/zone_overview.pb.go b/api/system/v1alpha1/zone_overview.pb.go
index bfdf0b4d6..3892ff13f 100644
--- a/api/system/v1alpha1/zone_overview.pb.go
+++ b/api/system/v1alpha1/zone_overview.pb.go
@@ -7,6 +7,7 @@
 package v1alpha1
 
 import (
+	_ "github.com/kumahq/kuma/api/mesh"
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
 	reflect "reflect"
@@ -82,23 +83,30 @@ var file_system_v1alpha1_zone_overview_proto_rawDesc = []byte{
 	0x0a, 0x23, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
 	0x31, 0x2f, 0x7a, 0x6f, 0x6e, 0x65, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x2e,
 	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x14, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x73, 0x79, 0x73, 0x74,
-	0x65, 0x6d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x1a, 0x1a, 0x73, 0x79, 0x73,
+	0x65, 0x6d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x1a, 0x12, 0x6d, 0x65, 0x73,
+	0x68, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
+	0x1a, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
+	0x2f, 0x7a, 0x6f, 0x6e, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x22, 0x73, 0x79, 0x73,
 	0x74, 0x65, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x7a, 0x6f, 0x6e,
-	0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x22, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2f,
-	0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x7a, 0x6f, 0x6e, 0x65, 0x5f, 0x69, 0x6e,
-	0x73, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x84, 0x01, 0x0a, 0x0c,
-	0x5a, 0x6f, 0x6e, 0x65, 0x4f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x12, 0x2e, 0x0a, 0x04,
-	0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6b, 0x75, 0x6d,
-	0x61, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
-	0x31, 0x2e, 0x5a, 0x6f, 0x6e, 0x65, 0x52, 0x04, 0x7a, 0x6f, 0x6e, 0x65, 0x12, 0x44, 0x0a, 0x0c,
-	0x7a, 0x6f, 0x6e, 0x65, 0x5f, 0x69, 0x6e, 0x73, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01,
-	0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d,
-	0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x5a, 0x6f, 0x6e, 0x65, 0x49, 0x6e,
-	0x73, 0x69, 0x67, 0x68, 0x74, 0x52, 0x0b, 0x7a, 0x6f, 0x6e, 0x65, 0x49, 0x6e, 0x73, 0x69, 0x67,
-	0x68, 0x74, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
-	0x2f, 0x6b, 0x75, 0x6d, 0x61, 0x68, 0x71, 0x2f, 0x6b, 0x75, 0x6d, 0x61, 0x2f, 0x61, 0x70, 0x69,
-	0x2f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
-	0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x65, 0x5f, 0x69, 0x6e, 0x73, 0x69, 0x67, 0x68, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22,
+	0xdc, 0x01, 0x0a, 0x0c, 0x5a, 0x6f, 0x6e, 0x65, 0x4f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77,
+	0x12, 0x2e, 0x0a, 0x04, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a,
+	0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x61,
+	0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x5a, 0x6f, 0x6e, 0x65, 0x52, 0x04, 0x7a, 0x6f, 0x6e, 0x65,
+	0x12, 0x44, 0x0a, 0x0c, 0x7a, 0x6f, 0x6e, 0x65, 0x5f, 0x69, 0x6e, 0x73, 0x69, 0x67, 0x68, 0x74,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x2e, 0x73, 0x79,
+	0x73, 0x74, 0x65, 0x6d, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x5a, 0x6f,
+	0x6e, 0x65, 0x49, 0x6e, 0x73, 0x69, 0x67, 0x68, 0x74, 0x52, 0x0b, 0x7a, 0x6f, 0x6e, 0x65, 0x49,
+	0x6e, 0x73, 0x69, 0x67, 0x68, 0x74, 0x3a, 0x56, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x16, 0x0a, 0x14,
+	0x5a, 0x6f, 0x6e, 0x65, 0x4f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0x52, 0x65, 0x73, 0x6f,
+	0x75, 0x72, 0x63, 0x65, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x0e, 0x12, 0x0c, 0x5a, 0x6f, 0x6e, 0x65,
+	0x4f, 0x76, 0x65, 0x72, 0x76, 0x69, 0x65, 0x77, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x08, 0x22, 0x06,
+	0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x02, 0x18, 0x01, 0xaa, 0x8c,
+	0x89, 0xa6, 0x01, 0x02, 0x30, 0x01, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x02, 0x28, 0x01, 0x42, 0x2c,
+	0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6b, 0x75, 0x6d,
+	0x61, 0x68, 0x71, 0x2f, 0x6b, 0x75, 0x6d, 0x61, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x79, 0x73,
+	0x74, 0x65, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
diff --git a/api/system/v1alpha1/zone_overview.proto b/api/system/v1alpha1/zone_overview.proto
index ee40399a6..2abd5ae9d 100644
--- a/api/system/v1alpha1/zone_overview.proto
+++ b/api/system/v1alpha1/zone_overview.proto
@@ -4,12 +4,20 @@ package kuma.system.v1alpha1;
 
 option go_package = "github.com/kumahq/kuma/api/system/v1alpha1";
 
+import "mesh/options.proto";
 import "system/v1alpha1/zone.proto";
 import "system/v1alpha1/zone_insight.proto";
 
 // ZoneOverview defines the projected state of a Zone.
 message ZoneOverview {
 
+  option (kuma.mesh.resource).name = "ZoneOverviewResource";
+  option (kuma.mesh.resource).type = "ZoneOverview";
+  option (kuma.mesh.resource).package = "system";
+  option (kuma.mesh.resource).global = true;
+  option (kuma.mesh.resource).skip_registration = true;
+  option (kuma.mesh.resource).skip_validation = true;
+
   Zone zone = 1;
 
   ZoneInsight zone_insight = 2;
diff --git a/app/kumactl/cmd/install/context/install_tracing_context.go b/app/kumactl/cmd/install/context/install_tracing_context.go
new file mode 100644
index 000000000..3abfeaef7
--- /dev/null
+++ b/app/kumactl/cmd/install/context/install_tracing_context.go
@@ -0,0 +1,17 @@
+package context
+
+type TracingTemplateArgs struct {
+	Namespace string
+}
+
+type InstallTracingContext struct {
+	TemplateArgs TracingTemplateArgs
+}
+
+func DefaultInstallTracingContext() InstallTracingContext {
+	return InstallTracingContext{
+		TemplateArgs: TracingTemplateArgs{
+			Namespace: "kuma-tracing",
+		},
+	}
+}
diff --git a/app/kumactl/cmd/install/install.go b/app/kumactl/cmd/install/install.go
index 3550b68e4..ef811354d 100644
--- a/app/kumactl/cmd/install/install.go
+++ b/app/kumactl/cmd/install/install.go
@@ -16,7 +16,7 @@ func NewInstallCmd(pctx *kumactl_cmd.RootContext) *cobra.Command {
 	cmd.AddCommand(newInstallControlPlaneCmd(&pctx.InstallCpContext))
 	cmd.AddCommand(newInstallCrdsCmd(&pctx.InstallCRDContext))
 	cmd.AddCommand(newInstallMetrics(pctx))
-	cmd.AddCommand(newInstallTracing())
+	cmd.AddCommand(newInstallTracing(pctx))
 	cmd.AddCommand(newInstallDNS())
 	cmd.AddCommand(newInstallLogging())
 	cmd.AddCommand(newInstallTransparentProxy())
diff --git a/app/kumactl/cmd/install/install_tracing.go b/app/kumactl/cmd/install/install_tracing.go
index 6e9af8b78..bd03cf79c 100644
--- a/app/kumactl/cmd/install/install_tracing.go
+++ b/app/kumactl/cmd/install/install_tracing.go
@@ -4,6 +4,8 @@ import (
 	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 
+	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
+
 	kumactl_data "github.com/kumahq/kuma/app/kumactl/data"
 	"github.com/kumahq/kuma/app/kumactl/pkg/install/data"
 	"github.com/kumahq/kuma/app/kumactl/pkg/install/k8s"
@@ -13,12 +15,8 @@ type tracingTemplateArgs struct {
 	Namespace string
 }
 
-func newInstallTracing() *cobra.Command {
-	args := struct {
-		Namespace string
-	}{
-		Namespace: "kuma-tracing",
-	}
+func newInstallTracing(pctx *kumactl_cmd.RootContext) *cobra.Command {
+	args := pctx.InstallTracingContext.TemplateArgs
 	cmd := &cobra.Command{
 		Use:   "tracing",
 		Short: "Install Tracing backend in Kubernetes cluster (Jaeger)",
diff --git a/app/kumactl/cmd/install/install_transparent_proxy.go b/app/kumactl/cmd/install/install_transparent_proxy.go
index f38e0cb0d..0d3ab3907 100644
--- a/app/kumactl/cmd/install/install_transparent_proxy.go
+++ b/app/kumactl/cmd/install/install_transparent_proxy.go
@@ -19,10 +19,9 @@ import (
 	"github.com/kumahq/kuma/pkg/transparentproxy/config"
 )
 
-type transparenProxyArgs struct {
+type transparentProxyArgs struct {
 	DryRun                 bool
 	Verbose                bool
-	ModifyIptables         bool
 	RedirectPortOutBound   string
 	RedirectInbound        bool
 	RedirectPortInBound    string
@@ -43,10 +42,9 @@ type transparenProxyArgs struct {
 var defaultCpIP = net.IPv4(0, 0, 0, 0)
 
 func newInstallTransparentProxy() *cobra.Command {
-	args := transparenProxyArgs{
+	args := transparentProxyArgs{
 		DryRun:                 false,
 		Verbose:                false,
-		ModifyIptables:         true,
 		RedirectPortOutBound:   "15001",
 		RedirectInbound:        true,
 		RedirectPortInBound:    "15006",
@@ -123,10 +121,14 @@ runuser -u kuma-dp -- \
 				return errors.Errorf("transparent proxy will work only on Linux OSes")
 			}
 
-			if args.ModifyIptables && args.User == "" && args.UID == "" {
+			if args.User == "" && args.UID == "" {
 				return errors.Errorf("--kuma-dp-user or --kuma-dp-uid should be supplied")
 			}
 
+			if args.RedirectAllDNSTraffic && args.RedirectDNS {
+				return errors.Errorf("one of --redirect-dns or --redirect-all-dns-traffic should be specified")
+			}
+
 			if args.RedirectAllDNSTraffic {
 				args.RedirectDNS = true
 			}
@@ -139,10 +141,8 @@ runuser -u kuma-dp -- \
 				return errors.Errorf("please supply a valid --kuma-cp-ip")
 			}
 
-			if args.ModifyIptables {
-				if err := modifyIpTables(cmd, &args); err != nil {
-					return err
-				}
+			if err := modifyIpTables(cmd, &args); err != nil {
+				return err
 			}
 
 			if !args.SkipResolvConf {
@@ -158,7 +158,6 @@ runuser -u kuma-dp -- \
 
 	cmd.Flags().BoolVar(&args.DryRun, "dry-run", args.DryRun, "dry run")
 	cmd.Flags().BoolVar(&args.Verbose, "verbose", args.Verbose, "verbose")
-	cmd.Flags().BoolVar(&args.ModifyIptables, "modify-iptables", args.ModifyIptables, "modify the host iptables to redirect the traffic to Envoy")
 	cmd.Flags().StringVar(&args.RedirectPortOutBound, "redirect-outbound-port", args.RedirectPortOutBound, "outbound port redirected to Envoy, as specified in dataplane's `networking.transparentProxying.redirectPortOutbound`")
 	cmd.Flags().BoolVar(&args.RedirectInbound, "redirect-inbound", args.RedirectInbound, "redirect the inbound traffic to the Envoy. Should be disabled for Gateway data plane proxies.")
 	cmd.Flags().StringVar(&args.RedirectPortInBound, "redirect-inbound-port", args.RedirectPortInBound, "inbound port redirected to Envoy, as specified in dataplane's `networking.transparentProxying.redirectPortInbound`")
@@ -167,8 +166,8 @@ runuser -u kuma-dp -- \
 	cmd.Flags().StringVar(&args.ExcludeOutboundPorts, "exclude-outbound-ports", args.ExcludeOutboundPorts, "a comma separated list of outbound ports to exclude from redirect to Envoy")
 	cmd.Flags().StringVar(&args.User, "kuma-dp-user", args.UID, "the user that will run kuma-dp")
 	cmd.Flags().StringVar(&args.UID, "kuma-dp-uid", args.UID, "the UID of the user that will run kuma-dp")
-	cmd.Flags().BoolVar(&args.RedirectDNS, "redirect-dns", args.RedirectDNS, "redirect all DNS requests to the servers in /etc/resolv.conf to a specified port")
-	cmd.Flags().BoolVar(&args.RedirectAllDNSTraffic, "redirect-all-dns-traffic", args.RedirectAllDNSTraffic, "redirect all DNS requests to a specified port. Implies --redirect-dns.")
+	cmd.Flags().BoolVar(&args.RedirectDNS, "redirect-dns", args.RedirectDNS, "redirect only DNS requests targeted to the servers listed in /etc/resolv.conf to a specified port")
+	cmd.Flags().BoolVar(&args.RedirectAllDNSTraffic, "redirect-all-dns-traffic", args.RedirectAllDNSTraffic, "redirect all DNS requests to a specified port")
 	cmd.Flags().StringVar(&args.AgentDNSListenerPort, "redirect-dns-port", args.AgentDNSListenerPort, "the port where the DNS agent is listening")
 	cmd.Flags().StringVar(&args.DNSUpstreamTargetChain, "redirect-dns-upstream-target-chain", args.DNSUpstreamTargetChain, "(optional) the iptables chain where the upstream DNS requests should be directed to. It is only applied for IP V4. Use with care.")
 	cmd.Flags().BoolVar(&args.SkipResolvConf, "skip-resolv-conf", args.SkipResolvConf, "skip modifying the host `/etc/resolv.conf`")
@@ -198,7 +197,7 @@ func findUidGid(uid, user string) (string, string, error) {
 	return u.Uid, u.Gid, nil
 }
 
-func modifyIpTables(cmd *cobra.Command, args *transparenProxyArgs) error {
+func modifyIpTables(cmd *cobra.Command, args *transparentProxyArgs) error {
 	tp := transparentproxy.DefaultTransparentProxy()
 
 	// best effort cleanup before we apply the rules (again?)
@@ -251,7 +250,7 @@ func modifyIpTables(cmd *cobra.Command, args *transparenProxyArgs) error {
 	return nil
 }
 
-func storeFirewalld(cmd *cobra.Command, args *transparenProxyArgs, output string) error {
+func storeFirewalld(cmd *cobra.Command, args *transparentProxyArgs, output string) error {
 	translator := firewalld.NewFirewalldIptablesTranslator(args.DryRun)
 	parser := regexp.MustCompile(`\* (?P<table>\w*)`)
 	rules := map[string][]string{}
@@ -292,7 +291,7 @@ func storeFirewalld(cmd *cobra.Command, args *transparenProxyArgs, output string
 	return nil
 }
 
-func modifyResolvConf(cmd *cobra.Command, args *transparenProxyArgs) error {
+func modifyResolvConf(cmd *cobra.Command, args *transparentProxyArgs) error {
 	kumaCPLine := fmt.Sprintf("nameserver %s", args.KumaCpIP.String())
 	content, err := ioutil.ReadFile("/etc/resolv.conf")
 	if err != nil {
diff --git a/app/kumactl/pkg/cmd/root_context.go b/app/kumactl/pkg/cmd/root_context.go
index e8b6b0007..8eb80b7b1 100644
--- a/app/kumactl/pkg/cmd/root_context.go
+++ b/app/kumactl/pkg/cmd/root_context.go
@@ -57,6 +57,7 @@ type RootContext struct {
 	InstallDemoContext                  install_context.InstallDemoContext
 	InstallGatewayKongContext           install_context.InstallGatewayKongContext
 	InstallGatewayKongEnterpriseContext install_context.InstallGatewayKongEnterpriseContext
+	InstallTracingContext               install_context.InstallTracingContext
 }
 
 func DefaultRootContext() *RootContext {
@@ -97,6 +98,7 @@ func DefaultRootContext() *RootContext {
 		InstallDemoContext:                  install_context.DefaultInstallDemoContext(),
 		InstallGatewayKongContext:           install_context.DefaultInstallGatewayKongContext(),
 		InstallGatewayKongEnterpriseContext: install_context.DefaultInstallGatewayKongEnterpriseContext(),
+		InstallTracingContext:               install_context.DefaultInstallTracingContext(),
 	}
 }
 
diff --git a/docs/cmd/kumactl/kumactl_install_transparent-proxy.md b/docs/cmd/kumactl/kumactl_install_transparent-proxy.md
index 590d02faf..cd0bd7d53 100644
--- a/docs/cmd/kumactl/kumactl_install_transparent-proxy.md
+++ b/docs/cmd/kumactl/kumactl_install_transparent-proxy.md
@@ -71,9 +71,8 @@ kumactl install transparent-proxy [flags]
       --kuma-cp-ip ip                                                                   the IP address of the Kuma CP which exposes the DNS service on port 53. (default 0.0.0.0)
       --kuma-dp-uid string                                                              the UID of the user that will run kuma-dp
       --kuma-dp-user string                                                             the user that will run kuma-dp
-      --modify-iptables                                                                 modify the host iptables to redirect the traffic to Envoy (default true)
-      --redirect-all-dns-traffic                                                        redirect all DNS requests to a specified port. Implies --redirect-dns.
-      --redirect-dns                                                                    redirect all DNS requests to the servers in /etc/resolv.conf to a specified port
+      --redirect-all-dns-traffic                                                        redirect all DNS requests to a specified port
+      --redirect-dns                                                                    redirect only DNS requests targeted to the servers listed in /etc/resolv.conf to a specified port
       --redirect-dns-port string                                                        the port where the DNS agent is listening (default "15053")
       --redirect-dns-upstream-target-chain string                                       (optional) the iptables chain where the upstream DNS requests should be directed to. It is only applied for IP V4. Use with care. (default "RETURN")
       --redirect-inbound                                                                redirect the inbound traffic to the Envoy. Should be disabled for Gateway data plane proxies. (default true)
diff --git a/mk/generate.mk b/mk/generate.mk
index 52429f664..812e4fa7f 100644
--- a/mk/generate.mk
+++ b/mk/generate.mk
@@ -23,12 +23,16 @@ clean/proto: ## Dev: Remove auto-generated Protobuf files
 
 .PHONY: generate
 generate:  ## Dev: Run code generators
-generate: clean/proto protoc/pkg/config/app/kumactl/v1alpha1 protoc/pkg/test/apis/sample/v1alpha1 protoc/plugins resources/mesh generate/deepcopy
+generate: clean/proto protoc/pkg/config/app/kumactl/v1alpha1 protoc/pkg/test/apis/sample/v1alpha1 protoc/plugins resources/mesh generate/deepcopy resources/system
 
 .PHONY: resources/mesh
 resources/mesh:
 	$(GO_RUN) ./tools/resource-gen.go -package mesh > pkg/core/resources/apis/mesh/resources.go
 
+.PHONY: resources/mesh
+resources/system:
+	$(GO_RUN) ./tools/resource-gen.go -package system > pkg/core/resources/apis/system/resources.go
+
 .PHONY: protoc/pkg/config/app/kumactl/v1alpha1
 protoc/pkg/config/app/kumactl/v1alpha1:
 	$(PROTOC_GO) pkg/config/app/kumactl/v1alpha1/*.proto
diff --git a/pkg/core/policy/connection_matcher.go b/pkg/core/policy/connection_matcher.go
index aba7e5777..9d91d8ce8 100644
--- a/pkg/core/policy/connection_matcher.go
+++ b/pkg/core/policy/connection_matcher.go
@@ -18,7 +18,7 @@ func (f ServiceIteratorFunc) Next() (core_xds.ServiceName, bool) {
 	return f()
 }
 
-func ToOutboundServicesOf(dataplane *mesh_core.DataplaneResource) ServiceIteratorFunc {
+func ToOutboundServicesOf(dataplane *mesh_core.DataplaneResource) ServiceIterator {
 	idx := 0
 	return ServiceIteratorFunc(func() (core_xds.ServiceName, bool) {
 		if len(dataplane.Spec.Networking.GetOutbound()) < idx {
@@ -34,7 +34,7 @@ func ToOutboundServicesOf(dataplane *mesh_core.DataplaneResource) ServiceIterato
 	})
 }
 
-func ToServicesOf(destinations core_xds.DestinationMap) ServiceIteratorFunc {
+func ToServicesOf(destinations core_xds.DestinationMap) ServiceIterator {
 	services := make([]core_xds.ServiceName, 0, len(destinations))
 	for service := range destinations {
 		services = append(services, service)
@@ -42,7 +42,7 @@ func ToServicesOf(destinations core_xds.DestinationMap) ServiceIteratorFunc {
 	return ToServices(services)
 }
 
-func ToServices(services []core_xds.ServiceName) ServiceIteratorFunc {
+func ToServices(services []core_xds.ServiceName) ServiceIterator {
 	idx := 0
 	return ServiceIteratorFunc(func() (core_xds.ServiceName, bool) {
 		if len(services) <= idx {
diff --git a/pkg/core/resources/apis/mesh/mesh_validator.go b/pkg/core/resources/apis/mesh/mesh_validator.go
index 6c7fc16b2..3517380c5 100644
--- a/pkg/core/resources/apis/mesh/mesh_validator.go
+++ b/pkg/core/resources/apis/mesh/mesh_validator.go
@@ -139,15 +139,36 @@ func validateTracingBackend(backend *mesh_proto.TracingBackend) validators.Valid
 	if backend.Name == "" {
 		verr.AddViolation("name", "cannot be empty")
 	}
-	if backend.GetType() != mesh_proto.TracingZipkinType {
-		verr.AddViolation("type", fmt.Sprintf("unknown backend type. Available backends: %q", mesh_proto.TracingZipkinType))
-	}
 	if backend.Sampling.GetValue() < 0.0 || backend.Sampling.GetValue() > 100.0 {
 		verr.AddViolation("sampling", "has to be in [0.0 - 100.0] range")
 	}
-	if backend.GetType() == mesh_proto.TracingZipkinType {
+	switch backend.GetType() {
+	case mesh_proto.TracingZipkinType:
 		verr.AddError("config", validateZipkin(backend.Conf))
+	case mesh_proto.TracingDatadogType:
+		verr.AddError("config", validateDatadog(backend.Conf))
+	default:
+		verr.AddViolation("type", fmt.Sprintf("unknown backend type. Available backends: %q, %q", mesh_proto.TracingZipkinType, mesh_proto.TracingDatadogType))
+	}
+	return verr
+}
+
+func validateDatadog(cfgStr *structpb.Struct) validators.ValidationError {
+	var verr validators.ValidationError
+	cfg := mesh_proto.DatadogTracingBackendConfig{}
+	if err := proto.ToTyped(cfgStr, &cfg); err != nil {
+		verr.AddViolation("", fmt.Sprintf("could not parse config: %s", err.Error()))
+		return verr
 	}
+
+	if cfg.Address == "" {
+		verr.AddViolation("address", "cannot be empty")
+	}
+
+	if cfg.Port > 0xFFFF || cfg.Port < 1 {
+		verr.AddViolation("port", "must be in the range 1 to 65535")
+	}
+
 	return verr
 }
 
diff --git a/pkg/core/resources/apis/system/config.go b/pkg/core/resources/apis/system/config.go
deleted file mode 100644
index 0b109dbb6..000000000
--- a/pkg/core/resources/apis/system/config.go
+++ /dev/null
@@ -1,101 +0,0 @@
-package system
-
-import (
-	"errors"
-
-	config_proto "github.com/kumahq/kuma/api/system/v1alpha1"
-	"github.com/kumahq/kuma/pkg/core/resources/model"
-	"github.com/kumahq/kuma/pkg/core/resources/registry"
-)
-
-const (
-	ConfigType model.ResourceType = "Config"
-)
-
-var _ model.Resource = &ConfigResource{}
-
-type ConfigResource struct {
-	Meta model.ResourceMeta
-	Spec *config_proto.Config
-}
-
-func NewConfigResource() *ConfigResource {
-	return &ConfigResource{
-		Spec: &config_proto.Config{},
-	}
-}
-
-func (t *ConfigResource) GetType() model.ResourceType {
-	return ConfigType
-}
-
-func (t *ConfigResource) GetMeta() model.ResourceMeta {
-	return t.Meta
-}
-
-func (t *ConfigResource) SetMeta(m model.ResourceMeta) {
-	t.Meta = m
-}
-
-func (t *ConfigResource) GetSpec() model.ResourceSpec {
-	return t.Spec
-}
-
-func (t *ConfigResource) SetSpec(spec model.ResourceSpec) error {
-	value, ok := spec.(*config_proto.Config)
-	if !ok {
-		return errors.New("invalid type of spec")
-	} else {
-		t.Spec = value
-		return nil
-	}
-}
-
-func (t *ConfigResource) Validate() error {
-	return nil
-}
-
-func (t *ConfigResource) Scope() model.ResourceScope {
-	return model.ScopeGlobal
-}
-
-var _ model.ResourceList = &ConfigResourceList{}
-
-type ConfigResourceList struct {
-	Items      []*ConfigResource
-	Pagination model.Pagination
-}
-
-func (l *ConfigResourceList) GetItems() []model.Resource {
-	res := make([]model.Resource, len(l.Items))
-	for i, elem := range l.Items {
-		res[i] = elem
-	}
-	return res
-}
-
-func (l *ConfigResourceList) GetItemType() model.ResourceType {
-	return ConfigType
-}
-
-func (l *ConfigResourceList) NewItem() model.Resource {
-	return NewConfigResource()
-}
-
-func (l *ConfigResourceList) AddItem(r model.Resource) error {
-	if trr, ok := r.(*ConfigResource); ok {
-		l.Items = append(l.Items, trr)
-		return nil
-	} else {
-		return model.ErrorInvalidItemType((*ConfigResource)(nil), r)
-	}
-}
-
-func (l *ConfigResourceList) GetPagination() *model.Pagination {
-	return &l.Pagination
-}
-
-func init() {
-	registry.RegisterType(NewConfigResource())
-	registry.RegistryListType(&ConfigResourceList{})
-}
diff --git a/pkg/core/resources/apis/system/resources.go b/pkg/core/resources/apis/system/resources.go
new file mode 100644
index 000000000..287a774f2
--- /dev/null
+++ b/pkg/core/resources/apis/system/resources.go
@@ -0,0 +1,474 @@
+// Generated by tools/resource-gen.
+// Run "make generate" to update this file.
+
+// nolint:whitespace
+package system
+
+import (
+	"fmt"
+
+	mesh_proto "github.com/kumahq/kuma/api/system/v1alpha1"
+	"github.com/kumahq/kuma/pkg/core/resources/model"
+	"github.com/kumahq/kuma/pkg/core/resources/registry"
+)
+
+const (
+	ConfigType model.ResourceType = "Config"
+)
+
+var _ model.Resource = &ConfigResource{}
+
+type ConfigResource struct {
+	Meta model.ResourceMeta
+	Spec *mesh_proto.Config
+}
+
+func NewConfigResource() *ConfigResource {
+	return &ConfigResource{
+		Spec: &mesh_proto.Config{},
+	}
+}
+
+func (t *ConfigResource) GetType() model.ResourceType {
+	return ConfigType
+}
+
+func (t *ConfigResource) GetMeta() model.ResourceMeta {
+	return t.Meta
+}
+
+func (t *ConfigResource) SetMeta(m model.ResourceMeta) {
+	t.Meta = m
+}
+
+func (t *ConfigResource) GetSpec() model.ResourceSpec {
+	return t.Spec
+}
+
+func (t *ConfigResource) Validate() error {
+	return nil
+}
+
+func (t *ConfigResource) SetSpec(spec model.ResourceSpec) error {
+	protoType, ok := spec.(*mesh_proto.Config)
+	if !ok {
+		return fmt.Errorf("invalid type %T for Spec", spec)
+	} else {
+		t.Spec = protoType
+		return nil
+	}
+}
+
+func (t *ConfigResource) Scope() model.ResourceScope {
+
+	return model.ScopeGlobal
+
+}
+
+var _ model.ResourceList = &ConfigResourceList{}
+
+type ConfigResourceList struct {
+	Items      []*ConfigResource
+	Pagination model.Pagination
+}
+
+func (l *ConfigResourceList) GetItems() []model.Resource {
+	res := make([]model.Resource, len(l.Items))
+	for i, elem := range l.Items {
+		res[i] = elem
+	}
+	return res
+}
+
+func (l *ConfigResourceList) GetItemType() model.ResourceType {
+	return ConfigType
+}
+
+func (l *ConfigResourceList) NewItem() model.Resource {
+	return NewConfigResource()
+}
+
+func (l *ConfigResourceList) AddItem(r model.Resource) error {
+	if trr, ok := r.(*ConfigResource); ok {
+		l.Items = append(l.Items, trr)
+		return nil
+	} else {
+		return model.ErrorInvalidItemType((*ConfigResource)(nil), r)
+	}
+}
+
+func (l *ConfigResourceList) GetPagination() *model.Pagination {
+	return &l.Pagination
+}
+
+func init() {
+	registry.RegisterType(NewConfigResource())
+	registry.RegistryListType(&ConfigResourceList{})
+}
+
+const (
+	SecretType model.ResourceType = "Secret"
+)
+
+var _ model.Resource = &SecretResource{}
+
+type SecretResource struct {
+	Meta model.ResourceMeta
+	Spec *mesh_proto.Secret
+}
+
+func NewSecretResource() *SecretResource {
+	return &SecretResource{
+		Spec: &mesh_proto.Secret{},
+	}
+}
+
+func (t *SecretResource) GetType() model.ResourceType {
+	return SecretType
+}
+
+func (t *SecretResource) GetMeta() model.ResourceMeta {
+	return t.Meta
+}
+
+func (t *SecretResource) SetMeta(m model.ResourceMeta) {
+	t.Meta = m
+}
+
+func (t *SecretResource) GetSpec() model.ResourceSpec {
+	return t.Spec
+}
+
+func (t *SecretResource) Validate() error {
+	return nil
+}
+
+func (t *SecretResource) SetSpec(spec model.ResourceSpec) error {
+	protoType, ok := spec.(*mesh_proto.Secret)
+	if !ok {
+		return fmt.Errorf("invalid type %T for Spec", spec)
+	} else {
+		t.Spec = protoType
+		return nil
+	}
+}
+
+func (t *SecretResource) Scope() model.ResourceScope {
+
+	return model.ScopeMesh
+
+}
+
+var _ model.ResourceList = &SecretResourceList{}
+
+type SecretResourceList struct {
+	Items      []*SecretResource
+	Pagination model.Pagination
+}
+
+func (l *SecretResourceList) GetItems() []model.Resource {
+	res := make([]model.Resource, len(l.Items))
+	for i, elem := range l.Items {
+		res[i] = elem
+	}
+	return res
+}
+
+func (l *SecretResourceList) GetItemType() model.ResourceType {
+	return SecretType
+}
+
+func (l *SecretResourceList) NewItem() model.Resource {
+	return NewSecretResource()
+}
+
+func (l *SecretResourceList) AddItem(r model.Resource) error {
+	if trr, ok := r.(*SecretResource); ok {
+		l.Items = append(l.Items, trr)
+		return nil
+	} else {
+		return model.ErrorInvalidItemType((*SecretResource)(nil), r)
+	}
+}
+
+func (l *SecretResourceList) GetPagination() *model.Pagination {
+	return &l.Pagination
+}
+
+func init() {
+	registry.RegisterType(NewSecretResource())
+	registry.RegistryListType(&SecretResourceList{})
+}
+
+const (
+	ZoneType model.ResourceType = "Zone"
+)
+
+var _ model.Resource = &ZoneResource{}
+
+type ZoneResource struct {
+	Meta model.ResourceMeta
+	Spec *mesh_proto.Zone
+}
+
+func NewZoneResource() *ZoneResource {
+	return &ZoneResource{
+		Spec: &mesh_proto.Zone{},
+	}
+}
+
+func (t *ZoneResource) GetType() model.ResourceType {
+	return ZoneType
+}
+
+func (t *ZoneResource) GetMeta() model.ResourceMeta {
+	return t.Meta
+}
+
+func (t *ZoneResource) SetMeta(m model.ResourceMeta) {
+	t.Meta = m
+}
+
+func (t *ZoneResource) GetSpec() model.ResourceSpec {
+	return t.Spec
+}
+
+func (t *ZoneResource) SetSpec(spec model.ResourceSpec) error {
+	protoType, ok := spec.(*mesh_proto.Zone)
+	if !ok {
+		return fmt.Errorf("invalid type %T for Spec", spec)
+	} else {
+		t.Spec = protoType
+		return nil
+	}
+}
+
+func (t *ZoneResource) Scope() model.ResourceScope {
+
+	return model.ScopeGlobal
+
+}
+
+var _ model.ResourceList = &ZoneResourceList{}
+
+type ZoneResourceList struct {
+	Items      []*ZoneResource
+	Pagination model.Pagination
+}
+
+func (l *ZoneResourceList) GetItems() []model.Resource {
+	res := make([]model.Resource, len(l.Items))
+	for i, elem := range l.Items {
+		res[i] = elem
+	}
+	return res
+}
+
+func (l *ZoneResourceList) GetItemType() model.ResourceType {
+	return ZoneType
+}
+
+func (l *ZoneResourceList) NewItem() model.Resource {
+	return NewZoneResource()
+}
+
+func (l *ZoneResourceList) AddItem(r model.Resource) error {
+	if trr, ok := r.(*ZoneResource); ok {
+		l.Items = append(l.Items, trr)
+		return nil
+	} else {
+		return model.ErrorInvalidItemType((*ZoneResource)(nil), r)
+	}
+}
+
+func (l *ZoneResourceList) GetPagination() *model.Pagination {
+	return &l.Pagination
+}
+
+func init() {
+	registry.RegisterType(NewZoneResource())
+	registry.RegistryListType(&ZoneResourceList{})
+}
+
+const (
+	ZoneInsightType model.ResourceType = "ZoneInsight"
+)
+
+var _ model.Resource = &ZoneInsightResource{}
+
+type ZoneInsightResource struct {
+	Meta model.ResourceMeta
+	Spec *mesh_proto.ZoneInsight
+}
+
+func NewZoneInsightResource() *ZoneInsightResource {
+	return &ZoneInsightResource{
+		Spec: &mesh_proto.ZoneInsight{},
+	}
+}
+
+func (t *ZoneInsightResource) GetType() model.ResourceType {
+	return ZoneInsightType
+}
+
+func (t *ZoneInsightResource) GetMeta() model.ResourceMeta {
+	return t.Meta
+}
+
+func (t *ZoneInsightResource) SetMeta(m model.ResourceMeta) {
+	t.Meta = m
+}
+
+func (t *ZoneInsightResource) GetSpec() model.ResourceSpec {
+	return t.Spec
+}
+
+func (t *ZoneInsightResource) Validate() error {
+	return nil
+}
+
+func (t *ZoneInsightResource) SetSpec(spec model.ResourceSpec) error {
+	protoType, ok := spec.(*mesh_proto.ZoneInsight)
+	if !ok {
+		return fmt.Errorf("invalid type %T for Spec", spec)
+	} else {
+		t.Spec = protoType
+		return nil
+	}
+}
+
+func (t *ZoneInsightResource) Scope() model.ResourceScope {
+
+	return model.ScopeGlobal
+
+}
+
+var _ model.ResourceList = &ZoneInsightResourceList{}
+
+type ZoneInsightResourceList struct {
+	Items      []*ZoneInsightResource
+	Pagination model.Pagination
+}
+
+func (l *ZoneInsightResourceList) GetItems() []model.Resource {
+	res := make([]model.Resource, len(l.Items))
+	for i, elem := range l.Items {
+		res[i] = elem
+	}
+	return res
+}
+
+func (l *ZoneInsightResourceList) GetItemType() model.ResourceType {
+	return ZoneInsightType
+}
+
+func (l *ZoneInsightResourceList) NewItem() model.Resource {
+	return NewZoneInsightResource()
+}
+
+func (l *ZoneInsightResourceList) AddItem(r model.Resource) error {
+	if trr, ok := r.(*ZoneInsightResource); ok {
+		l.Items = append(l.Items, trr)
+		return nil
+	} else {
+		return model.ErrorInvalidItemType((*ZoneInsightResource)(nil), r)
+	}
+}
+
+func (l *ZoneInsightResourceList) GetPagination() *model.Pagination {
+	return &l.Pagination
+}
+
+func init() {
+	registry.RegisterType(NewZoneInsightResource())
+	registry.RegistryListType(&ZoneInsightResourceList{})
+}
+
+const (
+	ZoneOverviewType model.ResourceType = "ZoneOverview"
+)
+
+var _ model.Resource = &ZoneOverviewResource{}
+
+type ZoneOverviewResource struct {
+	Meta model.ResourceMeta
+	Spec *mesh_proto.ZoneOverview
+}
+
+func NewZoneOverviewResource() *ZoneOverviewResource {
+	return &ZoneOverviewResource{
+		Spec: &mesh_proto.ZoneOverview{},
+	}
+}
+
+func (t *ZoneOverviewResource) GetType() model.ResourceType {
+	return ZoneOverviewType
+}
+
+func (t *ZoneOverviewResource) GetMeta() model.ResourceMeta {
+	return t.Meta
+}
+
+func (t *ZoneOverviewResource) SetMeta(m model.ResourceMeta) {
+	t.Meta = m
+}
+
+func (t *ZoneOverviewResource) GetSpec() model.ResourceSpec {
+	return t.Spec
+}
+
+func (t *ZoneOverviewResource) Validate() error {
+	return nil
+}
+
+func (t *ZoneOverviewResource) SetSpec(spec model.ResourceSpec) error {
+	protoType, ok := spec.(*mesh_proto.ZoneOverview)
+	if !ok {
+		return fmt.Errorf("invalid type %T for Spec", spec)
+	} else {
+		t.Spec = protoType
+		return nil
+	}
+}
+
+func (t *ZoneOverviewResource) Scope() model.ResourceScope {
+
+	return model.ScopeGlobal
+
+}
+
+var _ model.ResourceList = &ZoneOverviewResourceList{}
+
+type ZoneOverviewResourceList struct {
+	Items      []*ZoneOverviewResource
+	Pagination model.Pagination
+}
+
+func (l *ZoneOverviewResourceList) GetItems() []model.Resource {
+	res := make([]model.Resource, len(l.Items))
+	for i, elem := range l.Items {
+		res[i] = elem
+	}
+	return res
+}
+
+func (l *ZoneOverviewResourceList) GetItemType() model.ResourceType {
+	return ZoneOverviewType
+}
+
+func (l *ZoneOverviewResourceList) NewItem() model.Resource {
+	return NewZoneOverviewResource()
+}
+
+func (l *ZoneOverviewResourceList) AddItem(r model.Resource) error {
+	if trr, ok := r.(*ZoneOverviewResource); ok {
+		l.Items = append(l.Items, trr)
+		return nil
+	} else {
+		return model.ErrorInvalidItemType((*ZoneOverviewResource)(nil), r)
+	}
+}
+
+func (l *ZoneOverviewResourceList) GetPagination() *model.Pagination {
+	return &l.Pagination
+}
diff --git a/pkg/core/resources/apis/system/secret.go b/pkg/core/resources/apis/system/secret.go
deleted file mode 100644
index 739bbbab1..000000000
--- a/pkg/core/resources/apis/system/secret.go
+++ /dev/null
@@ -1,101 +0,0 @@
-package system
-
-import (
-	"errors"
-
-	system_proto "github.com/kumahq/kuma/api/system/v1alpha1"
-	"github.com/kumahq/kuma/pkg/core/resources/model"
-	"github.com/kumahq/kuma/pkg/core/resources/registry"
-)
-
-const (
-	SecretType model.ResourceType = "Secret"
-)
-
-var _ model.Resource = &SecretResource{}
-
-type SecretResource struct {
-	Meta model.ResourceMeta
-	Spec *system_proto.Secret
-}
-
-func NewSecretResource() *SecretResource {
-	return &SecretResource{
-		Spec: &system_proto.Secret{},
-	}
-}
-
-func (t *SecretResource) GetType() model.ResourceType {
-	return SecretType
-}
-
-func (t *SecretResource) GetMeta() model.ResourceMeta {
-	return t.Meta
-}
-
-func (t *SecretResource) SetMeta(m model.ResourceMeta) {
-	t.Meta = m
-}
-
-func (t *SecretResource) GetSpec() model.ResourceSpec {
-	return t.Spec
-}
-
-func (t *SecretResource) SetSpec(spec model.ResourceSpec) error {
-	value, ok := spec.(*system_proto.Secret)
-	if !ok {
-		return errors.New("invalid type of spec")
-	} else {
-		t.Spec = value
-		return nil
-	}
-}
-
-func (t *SecretResource) Validate() error {
-	return nil
-}
-
-func (t *SecretResource) Scope() model.ResourceScope {
-	return model.ScopeMesh
-}
-
-var _ model.ResourceList = &SecretResourceList{}
-
-type SecretResourceList struct {
-	Items      []*SecretResource
-	Pagination model.Pagination
-}
-
-func (l *SecretResourceList) GetItems() []model.Resource {
-	res := make([]model.Resource, len(l.Items))
-	for i, elem := range l.Items {
-		res[i] = elem
-	}
-	return res
-}
-
-func (l *SecretResourceList) GetItemType() model.ResourceType {
-	return SecretType
-}
-
-func (l *SecretResourceList) NewItem() model.Resource {
-	return NewSecretResource()
-}
-
-func (l *SecretResourceList) AddItem(r model.Resource) error {
-	if trr, ok := r.(*SecretResource); ok {
-		l.Items = append(l.Items, trr)
-		return nil
-	} else {
-		return model.ErrorInvalidItemType((*SecretResource)(nil), r)
-	}
-}
-
-func (l *SecretResourceList) GetPagination() *model.Pagination {
-	return &l.Pagination
-}
-
-func init() {
-	registry.RegisterType(NewSecretResource())
-	registry.RegistryListType(&SecretResourceList{})
-}
diff --git a/pkg/core/resources/apis/system/zone.go b/pkg/core/resources/apis/system/zone.go
deleted file mode 100644
index 8a4995c3a..000000000
--- a/pkg/core/resources/apis/system/zone.go
+++ /dev/null
@@ -1,97 +0,0 @@
-package system
-
-import (
-	"errors"
-
-	system_proto "github.com/kumahq/kuma/api/system/v1alpha1"
-	"github.com/kumahq/kuma/pkg/core/resources/model"
-	"github.com/kumahq/kuma/pkg/core/resources/registry"
-)
-
-const (
-	ZoneType model.ResourceType = "Zone"
-)
-
-var _ model.Resource = &ZoneResource{}
-
-type ZoneResource struct {
-	Meta model.ResourceMeta
-	Spec *system_proto.Zone
-}
-
-func NewZoneResource() *ZoneResource {
-	return &ZoneResource{
-		Spec: &system_proto.Zone{},
-	}
-}
-
-func (t *ZoneResource) GetType() model.ResourceType {
-	return ZoneType
-}
-
-func (t *ZoneResource) GetMeta() model.ResourceMeta {
-	return t.Meta
-}
-
-func (t *ZoneResource) SetMeta(m model.ResourceMeta) {
-	t.Meta = m
-}
-
-func (t *ZoneResource) GetSpec() model.ResourceSpec {
-	return t.Spec
-}
-
-func (t *ZoneResource) SetSpec(spec model.ResourceSpec) error {
-	value, ok := spec.(*system_proto.Zone)
-	if !ok {
-		return errors.New("invalid type of spec")
-	} else {
-		t.Spec = value
-		return nil
-	}
-}
-
-func (t *ZoneResource) Scope() model.ResourceScope {
-	return model.ScopeGlobal
-}
-
-var _ model.ResourceList = &ZoneResourceList{}
-
-type ZoneResourceList struct {
-	Items      []*ZoneResource
-	Pagination model.Pagination
-}
-
-func (l *ZoneResourceList) GetItems() []model.Resource {
-	res := make([]model.Resource, len(l.Items))
-	for i, elem := range l.Items {
-		res[i] = elem
-	}
-	return res
-}
-
-func (l *ZoneResourceList) GetItemType() model.ResourceType {
-	return ZoneType
-}
-
-func (l *ZoneResourceList) NewItem() model.Resource {
-	return NewZoneResource()
-}
-
-func (l *ZoneResourceList) AddItem(r model.Resource) error {
-	if trr, ok := r.(*ZoneResource); ok {
-		l.Items = append(l.Items, trr)
-		return nil
-	} else {
-		return model.ErrorInvalidItemType((*ZoneResource)(nil), r)
-	}
-}
-
-func (l *ZoneResourceList) GetPagination() *model.Pagination {
-	return &l.Pagination
-}
-
-func init() {
-	registry.RegisterType(NewZoneResource())
-	registry.RegistryListType(&ZoneResourceList{})
-}
diff --git a/pkg/core/resources/apis/system/zone_insight.go b/pkg/core/resources/apis/system/zone_insight.go
deleted file mode 100644
index fe12355e8..000000000
--- a/pkg/core/resources/apis/system/zone_insight.go
+++ /dev/null
@@ -1,101 +0,0 @@
-package system
-
-import (
-	"errors"
-
-	system_proto "github.com/kumahq/kuma/api/system/v1alpha1"
-	"github.com/kumahq/kuma/pkg/core/resources/model"
-	"github.com/kumahq/kuma/pkg/core/resources/registry"
-)
-
-const (
-	ZoneInsightType model.ResourceType = "ZoneInsight"
-)
-
-var _ model.Resource = &ZoneInsightResource{}
-
-type ZoneInsightResource struct {
-	Meta model.ResourceMeta
-	Spec *system_proto.ZoneInsight
-}
-
-func NewZoneInsightResource() *ZoneInsightResource {
-	return &ZoneInsightResource{
-		Spec: &system_proto.ZoneInsight{},
-	}
-}
-
-func (t *ZoneInsightResource) GetType() model.ResourceType {
-	return ZoneInsightType
-}
-
-func (t *ZoneInsightResource) GetMeta() model.ResourceMeta {
-	return t.Meta
-}
-
-func (t *ZoneInsightResource) SetMeta(m model.ResourceMeta) {
-	t.Meta = m
-}
-
-func (t *ZoneInsightResource) GetSpec() model.ResourceSpec {
-	return t.Spec
-}
-
-func (t *ZoneInsightResource) SetSpec(spec model.ResourceSpec) error {
-	value, ok := spec.(*system_proto.ZoneInsight)
-	if !ok {
-		return errors.New("invalid type of spec")
-	} else {
-		t.Spec = value
-		return nil
-	}
-}
-
-func (t *ZoneInsightResource) Validate() error {
-	return nil
-}
-
-func (t *ZoneInsightResource) Scope() model.ResourceScope {
-	return model.ScopeGlobal
-}
-
-var _ model.ResourceList = &ZoneInsightResourceList{}
-
-type ZoneInsightResourceList struct {
-	Items      []*ZoneInsightResource
-	Pagination model.Pagination
-}
-
-func (l *ZoneInsightResourceList) GetItems() []model.Resource {
-	res := make([]model.Resource, len(l.Items))
-	for i, elem := range l.Items {
-		res[i] = elem
-	}
-	return res
-}
-
-func (l *ZoneInsightResourceList) GetItemType() model.ResourceType {
-	return ZoneInsightType
-}
-
-func (l *ZoneInsightResourceList) NewItem() model.Resource {
-	return NewZoneInsightResource()
-}
-
-func (l *ZoneInsightResourceList) AddItem(r model.Resource) error {
-	if trr, ok := r.(*ZoneInsightResource); ok {
-		l.Items = append(l.Items, trr)
-		return nil
-	} else {
-		return model.ErrorInvalidItemType((*ZoneInsightResource)(nil), r)
-	}
-}
-
-func (l *ZoneInsightResourceList) GetPagination() *model.Pagination {
-	return &l.Pagination
-}
-
-func init() {
-	registry.RegisterType(NewZoneInsightResource())
-	registry.RegistryListType(&ZoneInsightResourceList{})
-}
diff --git a/pkg/core/resources/apis/system/zone_overview.go b/pkg/core/resources/apis/system/zone_overview.go
index bb89a2a4b..a7f0bddf4 100644
--- a/pkg/core/resources/apis/system/zone_overview.go
+++ b/pkg/core/resources/apis/system/zone_overview.go
@@ -1,99 +1,10 @@
 package system
 
 import (
-	"errors"
-
 	system_proto "github.com/kumahq/kuma/api/system/v1alpha1"
 	"github.com/kumahq/kuma/pkg/core/resources/model"
 )
 
-const (
-	ZoneOverviewType model.ResourceType = "ZoneOverview"
-)
-
-var _ model.Resource = &ZoneOverviewResource{}
-
-type ZoneOverviewResource struct {
-	Meta model.ResourceMeta
-	Spec *system_proto.ZoneOverview
-}
-
-func NewZoneOverviewResource() *ZoneOverviewResource {
-	return &ZoneOverviewResource{
-		Spec: &system_proto.ZoneOverview{},
-	}
-}
-
-func (t *ZoneOverviewResource) GetType() model.ResourceType {
-	return ZoneOverviewType
-}
-
-func (t *ZoneOverviewResource) GetMeta() model.ResourceMeta {
-	return t.Meta
-}
-
-func (t *ZoneOverviewResource) SetMeta(m model.ResourceMeta) {
-	t.Meta = m
-}
-
-func (t *ZoneOverviewResource) GetSpec() model.ResourceSpec {
-	return t.Spec
-}
-
-func (t *ZoneOverviewResource) SetSpec(spec model.ResourceSpec) error {
-	zoneOverview, ok := spec.(*system_proto.ZoneOverview)
-	if !ok {
-		return errors.New("invalid type of spec")
-	} else {
-		t.Spec = zoneOverview
-		return nil
-	}
-}
-
-func (t *ZoneOverviewResource) Validate() error {
-	return nil
-}
-
-func (t *ZoneOverviewResource) Scope() model.ResourceScope {
-	return model.ScopeGlobal
-}
-
-var _ model.ResourceList = &ZoneOverviewResourceList{}
-
-type ZoneOverviewResourceList struct {
-	Items      []*ZoneOverviewResource
-	Pagination model.Pagination
-}
-
-func (l *ZoneOverviewResourceList) GetItems() []model.Resource {
-	res := make([]model.Resource, len(l.Items))
-	for i, elem := range l.Items {
-		res[i] = elem
-	}
-	return res
-}
-
-func (l *ZoneOverviewResourceList) GetItemType() model.ResourceType {
-	return ZoneOverviewType
-}
-
-func (l *ZoneOverviewResourceList) NewItem() model.Resource {
-	return NewZoneOverviewResource()
-}
-
-func (l *ZoneOverviewResourceList) AddItem(r model.Resource) error {
-	if trr, ok := r.(*ZoneOverviewResource); ok {
-		l.Items = append(l.Items, trr)
-		return nil
-	} else {
-		return model.ErrorInvalidItemType((*ZoneOverviewResource)(nil), r)
-	}
-}
-
-func (l *ZoneOverviewResourceList) GetPagination() *model.Pagination {
-	return &l.Pagination
-}
-
 func NewZoneOverviews(zones ZoneResourceList, insights ZoneInsightResourceList) ZoneOverviewResourceList {
 	insightsByKey := map[model.ResourceKey]*ZoneInsightResource{}
 	for _, insight := range insights.Items {
diff --git a/pkg/kds/global/components.go b/pkg/kds/global/components.go
index 58b2433c1..493c6d2b1 100644
--- a/pkg/kds/global/components.go
+++ b/pkg/kds/global/components.go
@@ -30,11 +30,13 @@ import (
 )
 
 var (
-	kdsGlobalLog  = core.Log.WithName("kds-global")
+	kdsGlobalLog = core.Log.WithName("kds-global")
+
+	// ProvidedTypes lists the resource types provided by the Global
+	// CP to the Zone CP.
 	ProvidedTypes = []model.ResourceType{
 		core_mesh.CircuitBreakerType,
 		core_mesh.DataplaneType,
-		core_mesh.ZoneIngressType,
 		core_mesh.ExternalServiceType,
 		core_mesh.FaultInjectionType,
 		core_mesh.HealthCheckType,
@@ -47,15 +49,19 @@ var (
 		core_mesh.TrafficPermissionType,
 		core_mesh.TrafficRouteType,
 		core_mesh.TrafficTraceType,
-		system.SecretType,
-		system.GlobalSecretType,
+		core_mesh.ZoneIngressType,
 		system.ConfigType,
+		system.GlobalSecretType,
+		system.SecretType,
 	}
+
+	// ConsumedTypes lists the resource types consumed from the Zone
+	// CP by the Global CP.
 	ConsumedTypes = []model.ResourceType{
-		core_mesh.DataplaneType,
 		core_mesh.DataplaneInsightType,
-		core_mesh.ZoneIngressType,
+		core_mesh.DataplaneType,
 		core_mesh.ZoneIngressInsightType,
+		core_mesh.ZoneIngressType,
 	}
 )
 
diff --git a/pkg/kds/types.go b/pkg/kds/types.go
index 43d4732ef..6382892cb 100644
--- a/pkg/kds/types.go
+++ b/pkg/kds/types.go
@@ -8,17 +8,18 @@ import (
 )
 
 const (
-	googleApis   = "type.googleapis.com/"
+	googleApis = "type.googleapis.com/"
+
+	// KumaResource is the type URL of the KumaResource protobuf.
 	KumaResource = googleApis + "kuma.mesh.v1alpha1.KumaResource"
 )
 
 var (
+	// SupportedTypes is a list of Kuma types that may be exchanged by KDS peers.
 	SupportedTypes = []model.ResourceType{
 		mesh.CircuitBreakerType,
-		mesh.DataplaneType,
-		mesh.ZoneIngressType,
-		mesh.ZoneIngressInsightType,
 		mesh.DataplaneInsightType,
+		mesh.DataplaneType,
 		mesh.ExternalServiceType,
 		mesh.FaultInjectionType,
 		mesh.HealthCheckType,
@@ -31,8 +32,10 @@ var (
 		mesh.TrafficPermissionType,
 		mesh.TrafficRouteType,
 		mesh.TrafficTraceType,
-		system.SecretType,
-		system.GlobalSecretType,
+		mesh.ZoneIngressInsightType,
+		mesh.ZoneIngressType,
 		system.ConfigType,
+		system.GlobalSecretType,
+		system.SecretType,
 	}
 )
diff --git a/pkg/kds/zone/components.go b/pkg/kds/zone/components.go
index 7be4948c5..0867ed994 100644
--- a/pkg/kds/zone/components.go
+++ b/pkg/kds/zone/components.go
@@ -24,17 +24,22 @@ import (
 )
 
 var (
-	kdsZoneLog    = core.Log.WithName("kds-zone")
+	kdsZoneLog = core.Log.WithName("kds-zone")
+
+	// ProvidedTypes lists the resource types provided by the Zone
+	// CP to the Global CP.
 	ProvidedTypes = []model.ResourceType{
-		mesh.ZoneIngressType,
-		mesh.ZoneIngressInsightType,
-		mesh.DataplaneType,
 		mesh.DataplaneInsightType,
+		mesh.DataplaneType,
+		mesh.ZoneIngressInsightType,
+		mesh.ZoneIngressType,
 	}
+
+	// ConsumedTypes lists the resource types consumed from the
+	// Global CP by the Zone CP.
 	ConsumedTypes = []model.ResourceType{
 		mesh.CircuitBreakerType,
 		mesh.DataplaneType,
-		mesh.ZoneIngressType,
 		mesh.ExternalServiceType,
 		mesh.FaultInjectionType,
 		mesh.HealthCheckType,
@@ -47,9 +52,10 @@ var (
 		mesh.TrafficPermissionType,
 		mesh.TrafficRouteType,
 		mesh.TrafficTraceType,
-		system.SecretType,
-		system.GlobalSecretType,
+		mesh.ZoneIngressType,
 		system.ConfigType,
+		system.GlobalSecretType,
+		system.SecretType,
 	}
 )
 
diff --git a/pkg/sds/server/v3/server.go b/pkg/sds/server/v3/server.go
index cfa7c9c44..7d714c4e9 100644
--- a/pkg/sds/server/v3/server.go
+++ b/pkg/sds/server/v3/server.go
@@ -11,6 +11,7 @@ import (
 	"github.com/go-logr/logr"
 
 	"github.com/kumahq/kuma/pkg/core"
+	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	core_runtime "github.com/kumahq/kuma/pkg/core/runtime"
 	core_xds "github.com/kumahq/kuma/pkg/core/xds"
 	sds_ca "github.com/kumahq/kuma/pkg/sds/ca"
@@ -72,7 +73,8 @@ func RegisterSDS(rt core_runtime.Runtime, sdsMetrics *sds_metrics.Metrics) error
 }
 
 func syncTracker(reconciler *DataplaneReconciler, refresh time.Duration, sdsMetrics *sds_metrics.Metrics) (util_xds.Callbacks, error) {
-	return xds_callbacks.NewDataplaneSyncTracker(func(proxyId *core_xds.ProxyId, streamId int64) util_watchdog.Watchdog {
+	return xds_callbacks.DataplaneCallbacksToXdsCallbacks(xds_callbacks.NewDataplaneSyncTracker(func(key core_model.ResourceKey) util_watchdog.Watchdog {
+		proxyId := core_xds.FromResourceKey(key)
 		return &util_watchdog.SimpleWatchdog{
 			NewTicker: func() *time.Ticker {
 				sdsMetrics.IncrementActiveWatchdogs(envoy_common.APIV3)
@@ -83,20 +85,20 @@ func syncTracker(reconciler *DataplaneReconciler, refresh time.Duration, sdsMetr
 				defer func() {
 					sdsMetrics.SdsGeneration(envoy_common.APIV3).Observe(float64(core.Now().Sub(start).Milliseconds()))
 				}()
-				return reconciler.Reconcile(proxyId)
+				return reconciler.Reconcile(&proxyId)
 			},
 			OnError: func(err error) {
 				sdsMetrics.SdsGenerationsErrors(envoy_common.APIV3).Inc()
 				sdsServerLog.Error(err, "OnTick() failed")
 			},
 			OnStop: func() {
-				if err := reconciler.Cleanup(proxyId); err != nil {
-					sdsServerLog.Error(err, "could not cleanup sync", "dataplane", proxyId.ToResourceKey())
+				if err := reconciler.Cleanup(&proxyId); err != nil {
+					sdsServerLog.Error(err, "could not cleanup sync", "dataplane", key)
 				}
 				sdsMetrics.DecrementActiveWatchdogs(envoy_common.APIV3)
 			},
 		}
-	}), nil
+	})), nil
 }
 
 type hasher struct {
diff --git a/pkg/xds/envoy/listeners/configurers.go b/pkg/xds/envoy/listeners/configurers.go
index 1beb6e05f..e9bfaf9d9 100644
--- a/pkg/xds/envoy/listeners/configurers.go
+++ b/pkg/xds/envoy/listeners/configurers.go
@@ -25,10 +25,11 @@ func Kafka(statsName string) FilterChainBuilderOpt {
 	})
 }
 
-func Tracing(backend *mesh_proto.TracingBackend) FilterChainBuilderOpt {
+func Tracing(backend *mesh_proto.TracingBackend, service string) FilterChainBuilderOpt {
 	return FilterChainBuilderOptFunc(func(config *FilterChainBuilderConfig) {
 		config.AddV3(&v3.TracingConfigurer{
 			Backend: backend,
+			Service: service,
 		})
 	})
 }
diff --git a/pkg/xds/envoy/listeners/v3/tracing_configurer.go b/pkg/xds/envoy/listeners/v3/tracing_configurer.go
index 606d7bdc7..52ec214b6 100644
--- a/pkg/xds/envoy/listeners/v3/tracing_configurer.go
+++ b/pkg/xds/envoy/listeners/v3/tracing_configurer.go
@@ -18,6 +18,10 @@ import (
 
 type TracingConfigurer struct {
 	Backend *mesh_proto.TracingBackend
+
+	// Opaque string which envoy will assign to tracer collector cluster, on those
+	// which support association of named "service" tags on traces. Consumed by datadog.
+	Service string
 }
 
 var _ FilterChainConfigurer = &TracingConfigurer{}
@@ -41,11 +45,40 @@ func (c *TracingConfigurer) Configure(filterChain *envoy_listener.FilterChain) e
 				return err
 			}
 			hcm.Tracing.Provider = tracing
+		case mesh_proto.TracingDatadogType:
+			tracing, err := datadogConfig(c.Backend.Conf, c.Backend.Name, c.Service)
+			if err != nil {
+				return err
+			}
+			hcm.Tracing.Provider = tracing
 		}
 		return nil
 	})
 }
 
+func datadogConfig(cfgStr *structpb.Struct, backendName string, serviceName string) (*envoy_trace.Tracing_Http, error) {
+	cfg := mesh_proto.DatadogTracingBackendConfig{}
+	if err := proto.ToTyped(cfgStr, &cfg); err != nil {
+		return nil, errors.Wrap(err, "could not convert backend")
+	}
+
+	datadogConfig := envoy_trace.DatadogConfig{
+		CollectorCluster: names.GetTracingClusterName(backendName),
+		ServiceName:      serviceName,
+	}
+	datadogConfigAny, err := proto.MarshalAnyDeterministic(&datadogConfig)
+	if err != nil {
+		return nil, err
+	}
+	tracingConfig := &envoy_trace.Tracing_Http{
+		Name: "envoy.datadog",
+		ConfigType: &envoy_trace.Tracing_Http_TypedConfig{
+			TypedConfig: datadogConfigAny,
+		},
+	}
+	return tracingConfig, nil
+}
+
 func zipkinConfig(cfgStr *structpb.Struct, backendName string) (*envoy_trace.Tracing_Http, error) {
 	cfg := mesh_proto.ZipkinTracingBackendConfig{}
 	if err := proto.ToTyped(cfgStr, &cfg); err != nil {
diff --git a/pkg/xds/generator/inbound_proxy_generator.go b/pkg/xds/generator/inbound_proxy_generator.go
index 1b98ce35b..4154c8699 100644
--- a/pkg/xds/generator/inbound_proxy_generator.go
+++ b/pkg/xds/generator/inbound_proxy_generator.go
@@ -76,7 +76,7 @@ func (g InboundProxyGenerator) Generate(ctx xds_context.Context, proxy *model.Pr
 					Configure(envoy_listeners.HttpConnectionManager(localClusterName, true)).
 					Configure(envoy_listeners.FaultInjection(proxy.Policies.FaultInjections[endpoint])).
 					Configure(envoy_listeners.RateLimit(proxy.Policies.RateLimits[endpoint])).
-					Configure(envoy_listeners.Tracing(proxy.Policies.TracingBackend)).
+					Configure(envoy_listeners.Tracing(proxy.Policies.TracingBackend, service)).
 					Configure(envoy_listeners.HttpInboundRoutes(service, routes))
 			case mesh_core.ProtocolGRPC:
 				filterChainBuilder.
@@ -84,7 +84,7 @@ func (g InboundProxyGenerator) Generate(ctx xds_context.Context, proxy *model.Pr
 					Configure(envoy_listeners.GrpcStats()).
 					Configure(envoy_listeners.FaultInjection(proxy.Policies.FaultInjections[endpoint])).
 					Configure(envoy_listeners.RateLimit(proxy.Policies.RateLimits[endpoint])).
-					Configure(envoy_listeners.Tracing(proxy.Policies.TracingBackend)).
+					Configure(envoy_listeners.Tracing(proxy.Policies.TracingBackend, service)).
 					Configure(envoy_listeners.HttpInboundRoutes(service, routes))
 			case mesh_core.ProtocolKafka:
 				filterChainBuilder.
diff --git a/pkg/xds/generator/outbound_proxy_generator.go b/pkg/xds/generator/outbound_proxy_generator.go
index 59de16361..2c8e6d1a1 100644
--- a/pkg/xds/generator/outbound_proxy_generator.go
+++ b/pkg/xds/generator/outbound_proxy_generator.go
@@ -105,7 +105,7 @@ func (_ OutboundProxyGenerator) generateLDS(proxy *model.Proxy, routes envoy_com
 		case mesh_core.ProtocolGRPC:
 			filterChainBuilder.
 				Configure(envoy_listeners.HttpConnectionManager(serviceName, false)).
-				Configure(envoy_listeners.Tracing(proxy.Policies.TracingBackend)).
+				Configure(envoy_listeners.Tracing(proxy.Policies.TracingBackend, sourceService)).
 				Configure(envoy_listeners.HttpAccessLog(meshName, envoy_common.TrafficDirectionOutbound, sourceService, serviceName, proxy.Policies.Logs[serviceName], proxy)).
 				Configure(envoy_listeners.HttpOutboundRoute(serviceName, routes, proxy.Dataplane.Spec.TagSet())).
 				Configure(envoy_listeners.Retry(retryPolicy, protocol)).
@@ -113,7 +113,7 @@ func (_ OutboundProxyGenerator) generateLDS(proxy *model.Proxy, routes envoy_com
 		case mesh_core.ProtocolHTTP, mesh_core.ProtocolHTTP2:
 			filterChainBuilder.
 				Configure(envoy_listeners.HttpConnectionManager(serviceName, false)).
-				Configure(envoy_listeners.Tracing(proxy.Policies.TracingBackend)).
+				Configure(envoy_listeners.Tracing(proxy.Policies.TracingBackend, sourceService)).
 				Configure(envoy_listeners.HttpAccessLog(
 					meshName,
 					envoy_common.TrafficDirectionOutbound,
diff --git a/pkg/xds/generator/tracing_proxy_generator.go b/pkg/xds/generator/tracing_proxy_generator.go
index 6225d13c2..fdc5e941c 100644
--- a/pkg/xds/generator/tracing_proxy_generator.go
+++ b/pkg/xds/generator/tracing_proxy_generator.go
@@ -35,7 +35,14 @@ func (t TracingProxyGenerator) Generate(_ xds_context.Context, proxy *core_xds.P
 			return nil, errors.Wrap(err, "could not generate zipkin cluster")
 		}
 		resources.Add(res)
+	case mesh_proto.TracingDatadogType:
+		res, err := t.datadogCluster(proxy.Policies.TracingBackend, proxy.APIVersion)
+		if err != nil {
+			return nil, errors.Wrap(err, "could not generate datadog cluster")
+		}
+		resources.Add(res)
 	}
+
 	return resources, nil
 }
 
@@ -67,3 +74,28 @@ func (t TracingProxyGenerator) zipkinCluster(backend *mesh_proto.TracingBackend,
 		Resource: cluster,
 	}, nil
 }
+
+func (t TracingProxyGenerator) datadogCluster(backend *mesh_proto.TracingBackend, apiVersion envoy.APIVersion) (*core_xds.Resource, error) {
+	cfg := mesh_proto.DatadogTracingBackendConfig{}
+	if err := proto.ToTyped(backend.Conf, &cfg); err != nil {
+		return nil, errors.Wrap(err, "could not convert backend")
+	}
+
+	if cfg.Port > 0xFFFF || cfg.Port < 1 {
+		return nil, errors.Errorf("invalid Datadog port number %d. Must be in range 1-65535", cfg.Port)
+	}
+
+	clusterName := names.GetTracingClusterName(backend.Name)
+	cluster, err := clusters.NewClusterBuilder(apiVersion).
+		Configure(clusters.DNSCluster(clusterName, cfg.Address, cfg.Port)).
+		Build()
+	if err != nil {
+		return nil, err
+	}
+
+	return &core_xds.Resource{
+		Name:     clusterName,
+		Origin:   OriginTracing,
+		Resource: cluster,
+	}, nil
+}
diff --git a/pkg/xds/server/callbacks/connection_info_tracker.go b/pkg/xds/server/callbacks/connection_info_tracker.go
index edacd2700..346072809 100644
--- a/pkg/xds/server/callbacks/connection_info_tracker.go
+++ b/pkg/xds/server/callbacks/connection_info_tracker.go
@@ -7,8 +7,8 @@ import (
 	"github.com/pkg/errors"
 	"google.golang.org/grpc/metadata"
 
+	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	core_xds "github.com/kumahq/kuma/pkg/core/xds"
-	util_xds "github.com/kumahq/kuma/pkg/util/xds"
 	xds_context "github.com/kumahq/kuma/pkg/xds/context"
 )
 
@@ -16,26 +16,33 @@ const authorityHeader = ":authority"
 
 // ConnectionInfoTracker tracks the information about the connection itself from the data plane to the control plane
 type ConnectionInfoTracker struct {
-	util_xds.NoopCallbacks
 	sync.RWMutex
-	connectionInfos map[core_xds.StreamID]xds_context.ConnectionInfo
+	connectionInfos map[core_model.ResourceKey]*xds_context.ConnectionInfo
 }
 
-var _ util_xds.Callbacks = &ConnectionInfoTracker{}
+var _ DataplaneCallbacks = &ConnectionInfoTracker{}
 
 func NewConnectionInfoTracker() *ConnectionInfoTracker {
 	return &ConnectionInfoTracker{
-		connectionInfos: map[core_xds.StreamID]xds_context.ConnectionInfo{},
+		connectionInfos: map[core_model.ResourceKey]*xds_context.ConnectionInfo{},
 	}
 }
 
-func (c *ConnectionInfoTracker) ConnectionInfo(streamID core_xds.StreamID) xds_context.ConnectionInfo {
+func (c *ConnectionInfoTracker) ConnectionInfo(dpKey core_model.ResourceKey) *xds_context.ConnectionInfo {
 	c.RLock()
 	defer c.RUnlock()
-	return c.connectionInfos[streamID]
+	return c.connectionInfos[dpKey]
 }
 
-func (c *ConnectionInfoTracker) OnStreamOpen(ctx context.Context, streamID core_xds.StreamID, _ string) error {
+func (c *ConnectionInfoTracker) OnProxyReconnected(_ core_xds.StreamID, dpKey core_model.ResourceKey, ctx context.Context, _ core_xds.DataplaneMetadata) error {
+	return c.processConnectionInfo(dpKey, ctx)
+}
+
+func (c *ConnectionInfoTracker) OnProxyConnected(_ core_xds.StreamID, dpKey core_model.ResourceKey, ctx context.Context, _ core_xds.DataplaneMetadata) error {
+	return c.processConnectionInfo(dpKey, ctx)
+}
+
+func (c *ConnectionInfoTracker) processConnectionInfo(dpKey core_model.ResourceKey, ctx context.Context) error {
 	metadata, ok := metadata.FromIncomingContext(ctx)
 	if !ok {
 		return errors.New("request has no metadata")
@@ -48,13 +55,13 @@ func (c *ConnectionInfoTracker) OnStreamOpen(ctx context.Context, streamID core_
 	connInfo := xds_context.ConnectionInfo{
 		Authority: values[0],
 	}
-	c.connectionInfos[streamID] = connInfo
+	c.connectionInfos[dpKey] = &connInfo
 	c.Unlock()
 	return nil
 }
 
-func (c *ConnectionInfoTracker) OnStreamClosed(streamID core_xds.StreamID) {
+func (c *ConnectionInfoTracker) OnProxyDisconnected(_ core_xds.StreamID, dpKey core_model.ResourceKey) {
 	c.Lock()
-	delete(c.connectionInfos, streamID)
+	delete(c.connectionInfos, dpKey)
 	c.Unlock()
 }
diff --git a/pkg/xds/server/callbacks/dataplane_callbacks.go b/pkg/xds/server/callbacks/dataplane_callbacks.go
new file mode 100644
index 000000000..4f6554d3d
--- /dev/null
+++ b/pkg/xds/server/callbacks/dataplane_callbacks.go
@@ -0,0 +1,154 @@
+package callbacks
+
+import (
+	"context"
+	"sync"
+
+	"github.com/pkg/errors"
+
+	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
+	core_xds "github.com/kumahq/kuma/pkg/core/xds"
+	util_xds "github.com/kumahq/kuma/pkg/util/xds"
+)
+
+// DataplaneCallbacks are XDS callbacks that keep the context of Kuma Dataplane.
+// In the ideal world we could assume that one Dataplane has one xDS stream.
+// Due to race network latencies etc. there might be a situation when one Dataplane has many xDS streams for the short period of time.
+// Those callbacks helps us to deal with such situation.
+//
+// Keep in mind that it does not solve many xDS streams across many instances of the Control Plane.
+// If there are many instances of the Control Plane and Dataplane reconnects, there might be an old stream
+// in one instance of CP and a new stream in a new instance of CP.
+//
+// Those callbacks may be also used with SDS. In case of SDS, at this moment Envoy creates many SDS streams to the Control Plane.
+type DataplaneCallbacks interface {
+	// OnProxyConnected is executed when proxy is connected after it was disconnected before.
+	OnProxyConnected(streamID core_xds.StreamID, dpKey core_model.ResourceKey, ctx context.Context, metadata core_xds.DataplaneMetadata) error
+	// OnProxyReconnected is executed when proxy is already connected, but there is another stream.
+	// This can happen when there is a delay with closing the old connection from the proxy to the control plane.
+	OnProxyReconnected(streamID core_xds.StreamID, dpKey core_model.ResourceKey, ctx context.Context, metadata core_xds.DataplaneMetadata) error
+	// OnProxyDisconnected is executed only when the last stream of the proxy disconnects.
+	OnProxyDisconnected(streamID core_xds.StreamID, dpKey core_model.ResourceKey)
+}
+
+type xdsCallbacks struct {
+	callbacks DataplaneCallbacks
+	util_xds.NoopCallbacks
+
+	sync.RWMutex
+	dpStreams     map[core_xds.StreamID]dpStream
+	activeStreams map[core_model.ResourceKey]int
+}
+
+func DataplaneCallbacksToXdsCallbacks(callbacks DataplaneCallbacks) util_xds.Callbacks {
+	return &xdsCallbacks{
+		callbacks:     callbacks,
+		dpStreams:     map[core_xds.StreamID]dpStream{},
+		activeStreams: map[core_model.ResourceKey]int{},
+	}
+}
+
+type dpStream struct {
+	dp  *core_model.ResourceKey
+	ctx context.Context
+}
+
+var _ util_xds.Callbacks = &xdsCallbacks{}
+
+func (d *xdsCallbacks) OnStreamClosed(streamID core_xds.StreamID) {
+	d.Lock()
+	defer d.Unlock()
+	dpStream := d.dpStreams[streamID]
+	if dpKey := dpStream.dp; dpKey != nil {
+		d.activeStreams[*dpKey]--
+		if d.activeStreams[*dpKey] == 0 {
+			d.callbacks.OnProxyDisconnected(streamID, *dpKey)
+			delete(d.activeStreams, *dpKey)
+		}
+	}
+	delete(d.dpStreams, streamID)
+}
+
+func (d *xdsCallbacks) OnStreamRequest(streamID core_xds.StreamID, request util_xds.DiscoveryRequest) error {
+	if request.NodeId() == "" {
+		// from https://www.envoyproxy.io/docs/envoy/latest/api-docs/xds_protocol#ack-nack-and-versioning:
+		// Only the first request on a stream is guaranteed to carry the node identifier.
+		// The subsequent discovery requests on the same stream may carry an empty node identifier.
+		// This holds true regardless of the acceptance of the discovery responses on the same stream.
+		// The node identifier should always be identical if present more than once on the stream.
+		// It is sufficient to only check the first message for the node identifier as a result.
+		return nil
+	}
+
+	d.RLock()
+	alreadyProcessed := d.dpStreams[streamID].dp != nil
+	d.RUnlock()
+	if alreadyProcessed {
+		return nil
+	}
+
+	proxyId, err := core_xds.ParseProxyIdFromString(request.NodeId())
+	if err != nil {
+		return errors.Wrap(err, "invalid node ID")
+	}
+	dpKey := proxyId.ToResourceKey()
+	metadata := core_xds.DataplaneMetadataFromXdsMetadata(request.Metadata())
+	if metadata == nil {
+		return errors.New("metadata in xDS Node cannot be nil")
+	}
+
+	d.Lock()
+	// in case client will open 2 concurrent request for the same streamID then
+	// we don't to increment the counter twice, so checking once again that stream
+	// wasn't processed
+	alreadyProcessed = d.dpStreams[streamID].dp != nil
+	if alreadyProcessed {
+		return nil
+	}
+
+	dpStream := d.dpStreams[streamID]
+	dpStream.dp = &dpKey
+	d.dpStreams[streamID] = dpStream
+
+	activeStreams := d.activeStreams[dpKey]
+	d.activeStreams[dpKey]++
+	d.Unlock()
+
+	if activeStreams == 0 {
+		if err := d.callbacks.OnProxyConnected(streamID, dpKey, dpStream.ctx, *metadata); err != nil {
+			return err
+		}
+	} else {
+		if err := d.callbacks.OnProxyReconnected(streamID, dpKey, dpStream.ctx, *metadata); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (d *xdsCallbacks) OnStreamOpen(ctx context.Context, streamID core_xds.StreamID, _ string) error {
+	d.Lock()
+	defer d.Unlock()
+	dps := dpStream{
+		ctx: ctx,
+	}
+	d.dpStreams[streamID] = dps
+	return nil
+}
+
+// NoopDataplaneCallbacks are empty callbacks that helps to implement DataplaneCallbacks without need to implement every function.
+type NoopDataplaneCallbacks struct {
+}
+
+func (n *NoopDataplaneCallbacks) OnProxyReconnected(core_xds.StreamID, core_model.ResourceKey, context.Context, core_xds.DataplaneMetadata) error {
+	return nil
+}
+
+func (n *NoopDataplaneCallbacks) OnProxyConnected(core_xds.StreamID, core_model.ResourceKey, context.Context, core_xds.DataplaneMetadata) error {
+	return nil
+}
+
+func (n *NoopDataplaneCallbacks) OnProxyDisconnected(core_xds.StreamID, core_model.ResourceKey) {
+}
+
+var _ DataplaneCallbacks = &NoopDataplaneCallbacks{}
diff --git a/pkg/xds/server/callbacks/dataplane_lifecycle.go b/pkg/xds/server/callbacks/dataplane_lifecycle.go
index 8d06e1baf..b021d5aee 100644
--- a/pkg/xds/server/callbacks/dataplane_lifecycle.go
+++ b/pkg/xds/server/callbacks/dataplane_lifecycle.go
@@ -13,8 +13,7 @@ import (
 	"github.com/kumahq/kuma/pkg/core/resources/manager"
 	"github.com/kumahq/kuma/pkg/core/resources/model"
 	"github.com/kumahq/kuma/pkg/core/resources/store"
-	"github.com/kumahq/kuma/pkg/core/xds"
-	util_xds "github.com/kumahq/kuma/pkg/util/xds"
+	core_xds "github.com/kumahq/kuma/pkg/core/xds"
 )
 
 var lifecycleLog = core.Log.WithName("xds").WithName("dp-lifecycle")
@@ -29,28 +28,45 @@ var lifecycleLog = core.Log.WithName("xds").WithName("dp-lifecycle")
 //
 // This flow is optional, you may still want to go with 1. an example of this is Kubernetes deployment.
 type DataplaneLifecycle struct {
-	util_xds.NoopCallbacks
 	resManager manager.ResourceManager
-	// createdDpForStream stores map from StreamID to created ResourceKey of Dataplane.
-	// we store nil values for streams without Dataplane in metadata to avoid accessing metadata with every DiscoveryRequest
-	createdDpForStream map[xds.StreamID]*model.ResourceKey
-	proxyTypeForStream map[xds.StreamID]mesh_proto.ProxyType
-	sync.RWMutex       // protects createdDpForStream
-	shutdownCh         <-chan struct{}
+
+	sync.RWMutex         // protects createdDpByCallbacks
+	createdDpByCallbacks map[model.ResourceKey]mesh_proto.ProxyType
+	shutdownCh           <-chan struct{}
 }
 
-var _ util_xds.Callbacks = &DataplaneLifecycle{}
+func (d *DataplaneLifecycle) OnProxyConnected(streamID core_xds.StreamID, dpKey model.ResourceKey, _ context.Context, md core_xds.DataplaneMetadata) error {
+	return d.register(streamID, dpKey, md)
+}
 
-func NewDataplaneLifecycle(resManager manager.ResourceManager, shutdownCh <-chan struct{}) *DataplaneLifecycle {
-	return &DataplaneLifecycle{
-		resManager:         resManager,
-		createdDpForStream: map[xds.StreamID]*model.ResourceKey{},
-		proxyTypeForStream: map[xds.StreamID]mesh_proto.ProxyType{},
-		shutdownCh:         shutdownCh,
+func (d *DataplaneLifecycle) OnProxyReconnected(streamID core_xds.StreamID, dpKey model.ResourceKey, _ context.Context, md core_xds.DataplaneMetadata) error {
+	return d.register(streamID, dpKey, md)
+}
+
+func (d *DataplaneLifecycle) register(streamID core_xds.StreamID, dpKey model.ResourceKey, md core_xds.DataplaneMetadata) error {
+	if md.GetProxyType() == mesh_proto.DataplaneProxyType && md.GetDataplaneResource() != nil {
+		dp := md.GetDataplaneResource()
+		lifecycleLog.Info("registering dataplane", "dataplane", dp, "dataplaneKey", dpKey, "streamID", streamID)
+		if err := d.registerDataplane(dp); err != nil {
+			return errors.Wrap(err, "could not register dataplane passed in kuma-dp run")
+		}
+		d.createdDpByCallbacks[dpKey] = mesh_proto.DataplaneProxyType
+		return nil
+	}
+
+	if md.GetProxyType() == mesh_proto.IngressProxyType && md.GetZoneIngressResource() != nil {
+		zi := md.GetZoneIngressResource()
+		lifecycleLog.Info("registering zone ingress", "zoneIngress", zi, "zoneIngressKey", dpKey, "streamID", streamID)
+		if err := d.registerZoneIngress(zi); err != nil {
+			return errors.Wrap(err, "could not register zone ingress passed in kuma-dp run")
+		}
+		d.createdDpByCallbacks[dpKey] = mesh_proto.IngressProxyType
+		return nil
 	}
+	return nil
 }
 
-func (d *DataplaneLifecycle) OnStreamClosed(streamID int64) {
+func (d *DataplaneLifecycle) OnProxyDisconnected(streamID core_xds.StreamID, dpKey model.ResourceKey) {
 	// OnStreamClosed method could be called either in case data plane proxy is down or
 	// Kuma CP is gracefully shutting down. If Kuma CP is gracefully shutting down we
 	// must not delete Dataplane resource, data plane proxy will be reconnected to another
@@ -64,79 +80,37 @@ func (d *DataplaneLifecycle) OnStreamClosed(streamID int64) {
 
 	d.Lock()
 	defer d.Unlock()
-	key := d.createdDpForStream[streamID]
-	proxyType := d.proxyTypeForStream[streamID]
-	delete(d.createdDpForStream, streamID)
-	delete(d.proxyTypeForStream, streamID)
-
-	if key == nil {
+	proxyType, createdByCallbacks := d.createdDpByCallbacks[dpKey]
+	if !createdByCallbacks {
 		return
 	}
+	delete(d.createdDpByCallbacks, dpKey)
 
 	if proxyType == mesh_proto.DataplaneProxyType {
-		lifecycleLog.Info("unregistering dataplane", "dataplaneKey", key, "streamID", streamID)
-		if err := d.unregisterDataplane(*key); err != nil {
+		lifecycleLog.Info("unregistering dataplane", "dataplaneKey", dpKey, "streamID", streamID)
+		if err := d.unregisterDataplane(dpKey); err != nil {
 			lifecycleLog.Error(err, "could not unregister dataplane")
 		}
 		return
 	}
 
 	if proxyType == mesh_proto.IngressProxyType {
-		lifecycleLog.Info("unregistering zone ingress", "zoneIngressKey", key, "streamID", streamID)
-		if err := d.unregisterZoneIngress(*key); err != nil {
+		lifecycleLog.Info("unregistering zone ingress", "zoneIngressKey", dpKey, "streamID", streamID)
+		if err := d.unregisterZoneIngress(dpKey); err != nil {
 			lifecycleLog.Error(err, "could not unregister zone ingress")
 		}
 		return
 	}
 }
 
-func (d *DataplaneLifecycle) OnStreamRequest(streamID int64, request util_xds.DiscoveryRequest) error {
-	if request.NodeId() == "" { // Only the first request on a stream is guaranteed to carry the node identifier.
-		return nil
-	}
+var _ DataplaneCallbacks = &DataplaneLifecycle{}
 
-	if d.streamProcessed(streamID) {
-		return nil
-	}
-
-	d.Lock()
-	defer d.Unlock()
-
-	md := xds.DataplaneMetadataFromXdsMetadata(request.Metadata())
-
-	if md.GetProxyType() == mesh_proto.DataplaneProxyType && md.GetDataplaneResource() != nil {
-		dp := md.GetDataplaneResource()
-		lifecycleLog.Info("registering dataplane", "dataplane", dp, "streamID", streamID, "nodeID", request.NodeId())
-		if err := d.registerDataplane(dp); err != nil {
-			return errors.Wrap(err, "could not register dataplane passed in kuma-dp run")
-		}
-		key := model.MetaToResourceKey(dp.GetMeta())
-		d.createdDpForStream[streamID] = &key
-		d.proxyTypeForStream[streamID] = mesh_proto.DataplaneProxyType
-		return nil
-	}
-
-	if md.GetProxyType() == mesh_proto.IngressProxyType && md.GetZoneIngressResource() != nil {
-		zi := md.GetZoneIngressResource()
-		lifecycleLog.Info("registering zone ingress", "zoneIngress", zi, "streamID", streamID, "nodeID", request.NodeId())
-		if err := d.registerZoneIngress(zi); err != nil {
-			return errors.Wrap(err, "could not register zone ingress passed in kuma-dp run")
-		}
-		key := model.MetaToResourceKey(zi.GetMeta())
-		d.createdDpForStream[streamID] = &key
-		d.proxyTypeForStream[streamID] = mesh_proto.IngressProxyType
-		return nil
+func NewDataplaneLifecycle(resManager manager.ResourceManager, shutdownCh <-chan struct{}) *DataplaneLifecycle {
+	return &DataplaneLifecycle{
+		resManager:           resManager,
+		createdDpByCallbacks: map[model.ResourceKey]mesh_proto.ProxyType{},
+		shutdownCh:           shutdownCh,
 	}
-
-	d.createdDpForStream[streamID] = nil // put nil so we don't have to read metadata every time
-	return nil
-}
-
-func (d *DataplaneLifecycle) streamProcessed(streamID int64) bool {
-	d.RLock()
-	defer d.RUnlock()
-	_, ok := d.createdDpForStream[streamID]
-	return ok
 }
 
 func (d *DataplaneLifecycle) registerDataplane(dp *core_mesh.DataplaneResource) error {
diff --git a/pkg/xds/server/callbacks/dataplane_metadata_tracker.go b/pkg/xds/server/callbacks/dataplane_metadata_tracker.go
index 98631ed61..74a034cd3 100644
--- a/pkg/xds/server/callbacks/dataplane_metadata_tracker.go
+++ b/pkg/xds/server/callbacks/dataplane_metadata_tracker.go
@@ -1,57 +1,50 @@
 package callbacks
 
 import (
+	"context"
 	"sync"
 
-	"github.com/kumahq/kuma/pkg/core/xds"
-	util_xds "github.com/kumahq/kuma/pkg/util/xds"
+	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
+	core_xds "github.com/kumahq/kuma/pkg/core/xds"
 )
 
 type DataplaneMetadataTracker struct {
-	util_xds.NoopCallbacks
-	mutex             sync.RWMutex
-	metadataForStream map[int64]*xds.DataplaneMetadata
+	sync.RWMutex
+	metadataForDp map[core_model.ResourceKey]*core_xds.DataplaneMetadata
 }
 
+var _ DataplaneCallbacks = &DataplaneMetadataTracker{}
+
 func NewDataplaneMetadataTracker() *DataplaneMetadataTracker {
 	return &DataplaneMetadataTracker{
-		mutex:             sync.RWMutex{},
-		metadataForStream: map[int64]*xds.DataplaneMetadata{},
+		metadataForDp: map[core_model.ResourceKey]*core_xds.DataplaneMetadata{},
 	}
 }
 
-func (d *DataplaneMetadataTracker) Metadata(streamId int64) *xds.DataplaneMetadata {
-	d.mutex.RLock()
-	defer d.mutex.RUnlock()
-	metadata, found := d.metadataForStream[streamId]
-	if found {
-		return metadata
-	} else {
-		return &xds.DataplaneMetadata{}
-	}
+func (d *DataplaneMetadataTracker) Metadata(dpKey core_model.ResourceKey) *core_xds.DataplaneMetadata {
+	d.RLock()
+	defer d.RUnlock()
+	return d.metadataForDp[dpKey]
 }
 
-var _ util_xds.Callbacks = &DataplaneMetadataTracker{}
+func (d *DataplaneMetadataTracker) OnProxyReconnected(_ core_xds.StreamID, dpKey core_model.ResourceKey, _ context.Context, metadata core_xds.DataplaneMetadata) error {
+	d.storeMetadata(dpKey, metadata)
+	return nil
+}
 
-func (d *DataplaneMetadataTracker) OnStreamClosed(stream int64) {
-	d.mutex.Lock()
-	defer d.mutex.Unlock()
-	delete(d.metadataForStream, stream)
+func (d *DataplaneMetadataTracker) OnProxyConnected(_ core_xds.StreamID, dpKey core_model.ResourceKey, _ context.Context, metadata core_xds.DataplaneMetadata) error {
+	d.storeMetadata(dpKey, metadata)
+	return nil
 }
 
-func (d *DataplaneMetadataTracker) OnStreamRequest(stream int64, req util_xds.DiscoveryRequest) error {
-	if req.NodeId() == "" {
-		// from https://www.envoyproxy.io/docs/envoy/latest/api-docs/xds_protocol#ack-nack-and-versioning:
-		// Only the first request on a stream is guaranteed to carry the node identifier.
-		// The subsequent discovery requests on the same stream may carry an empty node identifier.
-		// This holds true regardless of the acceptance of the discovery responses on the same stream.
-		// The node identifier should always be identical if present more than once on the stream.
-		// It is sufficient to only check the first message for the node identifier as a result.
-		return nil
-	}
+func (d *DataplaneMetadataTracker) storeMetadata(dpKey core_model.ResourceKey, metadata core_xds.DataplaneMetadata) {
+	d.Lock()
+	defer d.Unlock()
+	d.metadataForDp[dpKey] = &metadata
+}
 
-	d.mutex.Lock()
-	defer d.mutex.Unlock()
-	d.metadataForStream[stream] = xds.DataplaneMetadataFromXdsMetadata(req.Metadata())
-	return nil
+func (d *DataplaneMetadataTracker) OnProxyDisconnected(_ core_xds.StreamID, dpKey core_model.ResourceKey) {
+	d.Lock()
+	defer d.Unlock()
+	delete(d.metadataForDp, dpKey)
 }
diff --git a/pkg/xds/server/callbacks/dataplane_sync_tracker.go b/pkg/xds/server/callbacks/dataplane_sync_tracker.go
index 5de6ae83a..7f819e00a 100644
--- a/pkg/xds/server/callbacks/dataplane_sync_tracker.go
+++ b/pkg/xds/server/callbacks/dataplane_sync_tracker.go
@@ -8,29 +8,22 @@ import (
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	core_xds "github.com/kumahq/kuma/pkg/core/xds"
 	util_watchdog "github.com/kumahq/kuma/pkg/util/watchdog"
-	util_xds "github.com/kumahq/kuma/pkg/util/xds"
 )
 
 var (
 	dataplaneSyncTrackerLog = core.Log.WithName("xds-server").WithName("dataplane-sync-tracker")
 )
 
-type NewDataplaneWatchdogFunc func(proxyId *core_xds.ProxyId, streamId core_xds.StreamID) util_watchdog.Watchdog
+type NewDataplaneWatchdogFunc func(key core_model.ResourceKey) util_watchdog.Watchdog
 
-func NewDataplaneSyncTracker(factoryFunc NewDataplaneWatchdogFunc) util_xds.Callbacks {
+func NewDataplaneSyncTracker(factoryFunc NewDataplaneWatchdogFunc) DataplaneCallbacks {
 	return &dataplaneSyncTracker{
 		newDataplaneWatchdog: factoryFunc,
-		streamsAssociation:   make(map[core_xds.StreamID]core_model.ResourceKey),
-		dpStreams:            make(map[core_model.ResourceKey]streams),
+		watchdogs:            map[core_model.ResourceKey]context.CancelFunc{},
 	}
 }
 
-var _ util_xds.Callbacks = &dataplaneSyncTracker{}
-
-type streams struct {
-	watchdogCancel context.CancelFunc
-	activeStreams  map[core_xds.StreamID]bool
-}
+var _ DataplaneCallbacks = &dataplaneSyncTracker{}
 
 // dataplaneSyncTracker tracks XDS streams that are connected to the CP and fire up a watchdog.
 // Watchdog should be run only once for given dataplane regardless of the number of streams.
@@ -39,77 +32,35 @@ type streams struct {
 // Node info can be (but does not have to be) carried only on the first XDS request. That's why need streamsAssociation map
 // that indicates that the stream was already associated
 type dataplaneSyncTracker struct {
-	newDataplaneWatchdog NewDataplaneWatchdogFunc
+	NoopDataplaneCallbacks
 
-	stdsync.RWMutex    // protects access to the fields below
-	streamsAssociation map[core_xds.StreamID]core_model.ResourceKey
-	dpStreams          map[core_model.ResourceKey]streams
-}
+	newDataplaneWatchdog NewDataplaneWatchdogFunc
 
-// OnStreamOpen is called once an xDS stream is open with a stream ID and the type URL (or "" for ADS).
-// Returning an error will end processing and close the stream. OnStreamClosed will still be called.
-func (t *dataplaneSyncTracker) OnStreamOpen(ctx context.Context, streamID core_xds.StreamID, typ string) error {
-	return nil
+	stdsync.RWMutex // protects access to the fields below
+	watchdogs       map[core_model.ResourceKey]context.CancelFunc
 }
 
-// OnStreamClosed is called immediately prior to closing an xDS stream with a stream ID.
-func (t *dataplaneSyncTracker) OnStreamClosed(streamID core_xds.StreamID) {
+func (t *dataplaneSyncTracker) OnProxyConnected(streamID core_xds.StreamID, dpKey core_model.ResourceKey, _ context.Context, _ core_xds.DataplaneMetadata) error {
+	// We use OnProxyConnected because there should be only one watchdog for given dataplane.
 	t.Lock()
 	defer t.Unlock()
 
-	dp, hasAssociation := t.streamsAssociation[streamID]
-	if hasAssociation {
-		delete(t.streamsAssociation, streamID)
-
-		streams := t.dpStreams[dp]
-		delete(streams.activeStreams, streamID)
-		if len(streams.activeStreams) == 0 { // no stream is active, cancel watchdog
-			if streams.watchdogCancel != nil {
-				streams.watchdogCancel()
-			}
-			delete(t.dpStreams, dp)
-		}
-	}
-}
-
-// OnStreamRequest is called once a request is received on a stream.
-// Returning an error will end processing and close the stream. OnStreamClosed will still be called.
-func (t *dataplaneSyncTracker) OnStreamRequest(streamID core_xds.StreamID, req util_xds.DiscoveryRequest) error {
-	t.RLock()
-	_, alreadyAssociated := t.streamsAssociation[streamID]
-	t.RUnlock()
-
-	if alreadyAssociated {
-		return nil
-	}
+	stopCh := make(chan struct{})
 
-	if proxyId, err := core_xds.ParseProxyIdFromString(req.NodeId()); err == nil {
-		dataplaneKey := proxyId.ToResourceKey()
-		t.Lock()
-		defer t.Unlock()
-		streams := t.dpStreams[dataplaneKey]
-		if streams.activeStreams == nil {
-			streams.activeStreams = map[core_xds.StreamID]bool{}
-		}
-		streams.activeStreams[streamID] = true
-		if streams.watchdogCancel == nil { // watchdog was not started yet
-			stopCh := make(chan struct{})
-			streams.watchdogCancel = func() {
-				close(stopCh)
-			}
-			// kick off watchdog for that Dataplane
-			go t.newDataplaneWatchdog(proxyId, streamID).Start(stopCh)
-			dataplaneSyncTrackerLog.V(1).Info("started Watchdog for a Dataplane", "streamid", streamID, "proxyId", proxyId, "dataplaneKey", dataplaneKey)
-		}
-		t.dpStreams[dataplaneKey] = streams
-		t.streamsAssociation[streamID] = dataplaneKey
-	} else {
-		dataplaneSyncTrackerLog.Error(err, "failed to parse Dataplane Id out of DiscoveryRequest", "streamid", streamID, "req", req)
+	t.watchdogs[dpKey] = func() {
+		dataplaneSyncTrackerLog.V(1).Info("stopping Watchdog for a Dataplane", "dpKey", dpKey, "streamID", streamID)
+		close(stopCh)
 	}
-
+	dataplaneSyncTrackerLog.V(1).Info("starting Watchdog for a Dataplane", "dpKey", dpKey, "streamID", streamID)
+	go t.newDataplaneWatchdog(dpKey).Start(stopCh)
 	return nil
 }
 
-// OnStreamResponse is called immediately prior to sending a response on a stream.
-func (t *dataplaneSyncTracker) OnStreamResponse(streamID core_xds.StreamID, req util_xds.DiscoveryRequest, resp util_xds.DiscoveryResponse) {
+func (t *dataplaneSyncTracker) OnProxyDisconnected(_ core_xds.StreamID, dpKey core_model.ResourceKey) {
+	t.Lock()
+	defer t.Unlock()
+	if cancelFn := t.watchdogs[dpKey]; cancelFn != nil {
+		cancelFn()
+	}
+	delete(t.watchdogs, dpKey)
 }
diff --git a/pkg/xds/server/v3/components.go b/pkg/xds/server/v3/components.go
index 7974153ba..e3d09b637 100644
--- a/pkg/xds/server/v3/components.go
+++ b/pkg/xds/server/v3/components.go
@@ -53,11 +53,11 @@ func RegisterXDS(
 	callbacks := util_xds_v3.CallbacksChain{
 		util_xds_v3.NewControlPlaneIdCallbacks(rt.GetInstanceId()),
 		util_xds_v3.AdaptCallbacks(statsCallbacks),
-		util_xds_v3.AdaptCallbacks(connectionInfoTracker),
+		util_xds_v3.AdaptCallbacks(xds_callbacks.DataplaneCallbacksToXdsCallbacks(connectionInfoTracker)),
 		util_xds_v3.AdaptCallbacks(authCallbacks),
-		util_xds_v3.AdaptCallbacks(xds_callbacks.NewDataplaneSyncTracker(watchdogFactory.New)),
-		util_xds_v3.AdaptCallbacks(metadataTracker),
-		util_xds_v3.AdaptCallbacks(xds_callbacks.NewDataplaneLifecycle(rt.ResourceManager(), rt.ShutdownCh())),
+		util_xds_v3.AdaptCallbacks(xds_callbacks.DataplaneCallbacksToXdsCallbacks(xds_callbacks.NewDataplaneSyncTracker(watchdogFactory.New))),
+		util_xds_v3.AdaptCallbacks(xds_callbacks.DataplaneCallbacksToXdsCallbacks(metadataTracker)),
+		util_xds_v3.AdaptCallbacks(xds_callbacks.DataplaneCallbacksToXdsCallbacks(xds_callbacks.NewDataplaneLifecycle(rt.ResourceManager(), rt.ShutdownCh()))),
 		util_xds_v3.AdaptCallbacks(DefaultDataplaneStatusTracker(rt)),
 		util_xds_v3.AdaptCallbacks(xds_callbacks.NewNackBackoff(rt.Config().XdsServer.NACKBackoff)),
 		newResourceWarmingForcer(xdsContext.Cache(), xdsContext.Hasher()),
diff --git a/pkg/xds/sync/dataplane_proxy_builder.go b/pkg/xds/sync/dataplane_proxy_builder.go
index 7e359ab75..597e3a107 100644
--- a/pkg/xds/sync/dataplane_proxy_builder.go
+++ b/pkg/xds/sync/dataplane_proxy_builder.go
@@ -39,7 +39,7 @@ type DataplaneProxyBuilder struct {
 	apiVersion envoy.APIVersion
 }
 
-func (p *DataplaneProxyBuilder) build(key core_model.ResourceKey, streamId int64, meshCtx *xds_context.MeshContext) (*xds.Proxy, error) {
+func (p *DataplaneProxyBuilder) build(key core_model.ResourceKey, meshCtx *xds_context.MeshContext) (*xds.Proxy, error) {
 	ctx := context.Background()
 
 	dp, err := p.resolveDataplane(ctx, key)
@@ -61,7 +61,7 @@ func (p *DataplaneProxyBuilder) build(key core_model.ResourceKey, streamId int64
 		Id:         xds.FromResourceKey(key),
 		APIVersion: p.apiVersion,
 		Dataplane:  dp,
-		Metadata:   p.MetadataTracker.Metadata(streamId),
+		Metadata:   p.MetadataTracker.Metadata(key),
 		Routing:    *routing,
 		Policies:   *matchedPolicies,
 	}
diff --git a/pkg/xds/sync/dataplane_watchdog.go b/pkg/xds/sync/dataplane_watchdog.go
index 72a7c7a8e..f84ba2951 100644
--- a/pkg/xds/sync/dataplane_watchdog.go
+++ b/pkg/xds/sync/dataplane_watchdog.go
@@ -4,6 +4,7 @@ import (
 	"context"
 
 	"github.com/go-logr/logr"
+	"github.com/pkg/errors"
 
 	mesh_core "github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	"github.com/kumahq/kuma/pkg/core/resources/store"
@@ -29,9 +30,8 @@ type DataplaneWatchdogDependencies struct {
 
 type DataplaneWatchdog struct {
 	DataplaneWatchdogDependencies
-	key      core_model.ResourceKey
-	streamId int64
-	log      logr.Logger
+	key core_model.ResourceKey
+	log logr.Logger
 
 	// state of watchdog
 	lastHash         string // last Mesh hash that was used to **successfully** generate Reconcile Envoy config
@@ -39,21 +39,24 @@ type DataplaneWatchdog struct {
 	proxyTypeSettled bool
 }
 
-func NewDataplaneWatchdog(deps DataplaneWatchdogDependencies, proxyId *core_xds.ProxyId, streamId int64) *DataplaneWatchdog {
+func NewDataplaneWatchdog(deps DataplaneWatchdogDependencies, dpKey core_model.ResourceKey) *DataplaneWatchdog {
 	return &DataplaneWatchdog{
 		DataplaneWatchdogDependencies: deps,
-		key:                           proxyId.ToResourceKey(),
-		streamId:                      streamId,
-		log:                           core.Log.WithValues("key", proxyId.ToResourceKey(), "streamID", streamId),
+		key:                           dpKey,
+		log:                           core.Log.WithValues("key", dpKey),
 		proxyTypeSettled:              false,
 	}
 }
 
 func (d *DataplaneWatchdog) Sync() error {
 	ctx := context.Background()
+	metadata := d.metadataTracker.Metadata(d.key)
+	if metadata == nil {
+		return errors.New("metadata cannot be nil")
+	}
 
 	if d.dpType == "" {
-		d.dpType = d.metadataTracker.Metadata(d.streamId).GetProxyType()
+		d.dpType = metadata.GetProxyType()
 	}
 	// backwards compatibility
 	if d.dpType == mesh_proto.DataplaneProxyType && !d.proxyTypeSettled {
@@ -102,11 +105,11 @@ func (d *DataplaneWatchdog) syncDataplane() error {
 	}
 	d.log.V(1).Info("snapshot hash updated, reconcile", "prev", d.lastHash, "current", snapshotHash)
 
-	envoyCtx, err := d.xdsContextBuilder.buildMeshedContext(d.streamId, d.key.Mesh, d.lastHash)
+	envoyCtx, err := d.xdsContextBuilder.buildMeshedContext(d.key, d.lastHash)
 	if err != nil {
 		return err
 	}
-	proxy, err := d.dataplaneProxyBuilder.build(d.key, d.streamId, &envoyCtx.Mesh)
+	proxy, err := d.dataplaneProxyBuilder.build(d.key, &envoyCtx.Mesh)
 	if err != nil {
 		return err
 	}
@@ -119,8 +122,8 @@ func (d *DataplaneWatchdog) syncDataplane() error {
 
 // syncIngress synces state of Ingress Dataplane. Notice that it does not use Mesh Hash yet because Ingress supports many Meshes.
 func (d *DataplaneWatchdog) syncIngress() error {
-	envoyCtx := d.xdsContextBuilder.buildContext(d.streamId)
-	proxy, err := d.ingressProxyBuilder.build(d.key, d.streamId)
+	envoyCtx := d.xdsContextBuilder.buildContext(d.key)
+	proxy, err := d.ingressProxyBuilder.build(d.key)
 	if err != nil {
 		return err
 	}
diff --git a/pkg/xds/sync/dataplane_watchdog_factory.go b/pkg/xds/sync/dataplane_watchdog_factory.go
index e7b06eada..c5f3aded3 100644
--- a/pkg/xds/sync/dataplane_watchdog_factory.go
+++ b/pkg/xds/sync/dataplane_watchdog_factory.go
@@ -4,7 +4,7 @@ import (
 	"time"
 
 	"github.com/kumahq/kuma/pkg/core"
-	core_xds "github.com/kumahq/kuma/pkg/core/xds"
+	"github.com/kumahq/kuma/pkg/core/resources/model"
 	util_watchdog "github.com/kumahq/kuma/pkg/util/watchdog"
 	xds_metrics "github.com/kumahq/kuma/pkg/xds/metrics"
 )
@@ -28,9 +28,9 @@ func NewDataplaneWatchdogFactory(
 	}, nil
 }
 
-func (d *dataplaneWatchdogFactory) New(proxyId *core_xds.ProxyId, streamId int64) util_watchdog.Watchdog {
-	log := xdsServerLog.WithName("dataplane-sync-watchdog").WithValues("dataplaneKey", proxyId.ToResourceKey())
-	dataplaneWatchdog := NewDataplaneWatchdog(d.deps, proxyId, streamId)
+func (d *dataplaneWatchdogFactory) New(dpKey model.ResourceKey) util_watchdog.Watchdog {
+	log := xdsServerLog.WithName("dataplane-sync-watchdog").WithValues("dataplaneKey", dpKey)
+	dataplaneWatchdog := NewDataplaneWatchdog(d.deps, dpKey)
 	return &util_watchdog.SimpleWatchdog{
 		NewTicker: func() *time.Ticker {
 			return time.NewTicker(d.refreshInterval)
diff --git a/pkg/xds/sync/ingress_proxy_builder.go b/pkg/xds/sync/ingress_proxy_builder.go
index 54cf7edc6..199b863b9 100644
--- a/pkg/xds/sync/ingress_proxy_builder.go
+++ b/pkg/xds/sync/ingress_proxy_builder.go
@@ -23,7 +23,7 @@ type IngressProxyBuilder struct {
 	apiVersion envoy.APIVersion
 }
 
-func (p *IngressProxyBuilder) build(key core_model.ResourceKey, streamId int64) (*xds.Proxy, error) {
+func (p *IngressProxyBuilder) build(key core_model.ResourceKey) (*xds.Proxy, error) {
 	ctx := context.Background()
 
 	zoneIngress, err := p.getZoneIngress(key)
@@ -50,7 +50,7 @@ func (p *IngressProxyBuilder) build(key core_model.ResourceKey, streamId int64)
 		Id:          xds.FromResourceKey(key),
 		APIVersion:  p.apiVersion,
 		ZoneIngress: zoneIngress,
-		Metadata:    p.MetadataTracker.Metadata(streamId),
+		Metadata:    p.MetadataTracker.Metadata(key),
 		Routing:     *routing,
 	}
 	return proxy, nil
diff --git a/pkg/xds/sync/interfaces.go b/pkg/xds/sync/interfaces.go
index 576801f2c..4b41647ba 100644
--- a/pkg/xds/sync/interfaces.go
+++ b/pkg/xds/sync/interfaces.go
@@ -1,17 +1,18 @@
 package sync
 
 import (
+	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	core_xds "github.com/kumahq/kuma/pkg/core/xds"
 	util_watchdog "github.com/kumahq/kuma/pkg/util/watchdog"
 	xds_context "github.com/kumahq/kuma/pkg/xds/context"
 )
 
 type DataplaneMetadataTracker interface {
-	Metadata(streamID core_xds.StreamID) *core_xds.DataplaneMetadata
+	Metadata(dpKey core_model.ResourceKey) *core_xds.DataplaneMetadata
 }
 
 type ConnectionInfoTracker interface {
-	ConnectionInfo(streamID core_xds.StreamID) xds_context.ConnectionInfo
+	ConnectionInfo(dpKey core_model.ResourceKey) *xds_context.ConnectionInfo
 }
 
 // SnapshotReconciler reconciles Envoy XDS configuration (Snapshot) by executing all generators (pkg/xds/generator)
@@ -22,5 +23,5 @@ type SnapshotReconciler interface {
 
 // DataplaneWatchdogFactory returns a Watchdog that creates a new XdsContext and Proxy and executes SnapshotReconciler if there is any change
 type DataplaneWatchdogFactory interface {
-	New(proxyId *core_xds.ProxyId, streamId int64) util_watchdog.Watchdog
+	New(dpKey core_model.ResourceKey) util_watchdog.Watchdog
 }
diff --git a/pkg/xds/sync/xds_context_builder.go b/pkg/xds/sync/xds_context_builder.go
index 091293085..37dee40ff 100644
--- a/pkg/xds/sync/xds_context_builder.go
+++ b/pkg/xds/sync/xds_context_builder.go
@@ -3,13 +3,12 @@ package sync
 import (
 	"context"
 
-	"github.com/kumahq/kuma/pkg/envoy/admin"
-
 	"github.com/kumahq/kuma/pkg/core/dns/lookup"
 	core_mesh "github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	"github.com/kumahq/kuma/pkg/core/resources/manager"
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
+	"github.com/kumahq/kuma/pkg/envoy/admin"
 	xds_context "github.com/kumahq/kuma/pkg/xds/context"
 	xds_topology "github.com/kumahq/kuma/pkg/xds/topology"
 )
@@ -39,16 +38,16 @@ func newXDSContextBuilder(
 	}
 }
 
-func (c *xdsContextBuilder) buildMeshedContext(streamId int64, meshName string, meshHash string) (*xds_context.Context, error) {
+func (c *xdsContextBuilder) buildMeshedContext(dpKey core_model.ResourceKey, meshHash string) (*xds_context.Context, error) {
 	ctx := context.Background()
-	xdsCtx := c.buildContext(streamId)
+	xdsCtx := c.buildContext(dpKey)
 
 	mesh := core_mesh.NewMeshResource()
-	if err := c.resManager.Get(ctx, mesh, core_store.GetByKey(meshName, core_model.NoMesh)); err != nil {
+	if err := c.resManager.Get(ctx, mesh, core_store.GetByKey(dpKey.Mesh, core_model.NoMesh)); err != nil {
 		return nil, err
 	}
 
-	dataplanes, err := xds_topology.GetDataplanes(syncLog, context.Background(), c.resManager, c.lookupIP, meshName)
+	dataplanes, err := xds_topology.GetDataplanes(syncLog, context.Background(), c.resManager, c.lookupIP, dpKey.Mesh)
 	if err != nil {
 		return nil, err
 	}
@@ -60,10 +59,10 @@ func (c *xdsContextBuilder) buildMeshedContext(streamId int64, meshName string,
 	return xdsCtx, nil
 }
 
-func (c *xdsContextBuilder) buildContext(streamId int64) *xds_context.Context {
+func (c *xdsContextBuilder) buildContext(dpKey core_model.ResourceKey) *xds_context.Context {
 	return &xds_context.Context{
 		ControlPlane:     c.cpContext,
-		ConnectionInfo:   c.connectionInfoTracker.ConnectionInfo(streamId),
+		ConnectionInfo:   *c.connectionInfoTracker.ConnectionInfo(dpKey), // todo handle nil
 		Mesh:             xds_context.MeshContext{},
 		EnvoyAdminClient: c.envoyAdminClient,
 	}
diff --git a/tools/resource-gen.go b/tools/resource-gen.go
index 92d5facf8..026af2849 100644
--- a/tools/resource-gen.go
+++ b/tools/resource-gen.go
@@ -231,12 +231,12 @@ package %s
 import (
 	"fmt"
 
-	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
+	mesh_proto "github.com/kumahq/kuma/api/%s/v1alpha1"
 	"github.com/kumahq/kuma/pkg/core/resources/model"
 	"github.com/kumahq/kuma/pkg/core/resources/registry"
 )
 
-`, Package)
+`, Package, Package)
 
 	var types []protoreflect.MessageType
 


Test patch:
diff --git a/app/kumactl/cmd/completion/testdata/bash.golden b/app/kumactl/cmd/completion/testdata/bash.golden
index 6ea47a745..8002d6421 100644
--- a/app/kumactl/cmd/completion/testdata/bash.golden
+++ b/app/kumactl/cmd/completion/testdata/bash.golden
@@ -2914,8 +2914,6 @@ _kumactl_install_transparent-proxy()
     two_word_flags+=("--kuma-dp-user")
     local_nonpersistent_flags+=("--kuma-dp-user")
     local_nonpersistent_flags+=("--kuma-dp-user=")
-    flags+=("--modify-iptables")
-    local_nonpersistent_flags+=("--modify-iptables")
     flags+=("--redirect-all-dns-traffic")
     local_nonpersistent_flags+=("--redirect-all-dns-traffic")
     flags+=("--redirect-dns")
diff --git a/app/kumactl/cmd/install/install_transparent_proxy_test.go b/app/kumactl/cmd/install/install_transparent_proxy_test.go
index d5640dccc..37735a41e 100644
--- a/app/kumactl/cmd/install/install_transparent_proxy_test.go
+++ b/app/kumactl/cmd/install/install_transparent_proxy_test.go
@@ -94,4 +94,29 @@ var _ = Describe("kumactl install tracing", func() {
 			goldenFile: "install-transparent-proxy.overrides.golden.txt",
 		}),
 	)
+
+	DescribeTable("should return error",
+		func(given testCase) {
+			// given
+			rootCmd := cmd.DefaultRootCmd()
+			rootCmd.SetArgs(append([]string{"install", "transparent-proxy", "--dry-run"}, given.extraArgs...))
+			rootCmd.SetOut(stdout)
+			rootCmd.SetErr(stderr)
+
+			// when
+			err := rootCmd.Execute()
+			// then
+			Expect(err).To(HaveOccurred())
+			// and
+			Expect(stderr.String()).To(ContainSubstring("one of --redirect-dns or --redirect-all-dns-traffic should be specified"))
+		},
+		Entry("should generate defaults with username", testCase{
+			extraArgs: []string{
+				"--kuma-dp-user", "root",
+				"--redirect-dns",
+				"--redirect-all-dns-traffic",
+			},
+			goldenFile: "install-transparent-proxy.defaults.golden.txt",
+		}),
+	)
 })
diff --git a/pkg/core/resources/apis/mesh/mesh_validator_test.go b/pkg/core/resources/apis/mesh/mesh_validator_test.go
index c76a6c562..3163a9f60 100644
--- a/pkg/core/resources/apis/mesh/mesh_validator_test.go
+++ b/pkg/core/resources/apis/mesh/mesh_validator_test.go
@@ -404,7 +404,7 @@ var _ = Describe("Mesh", func() {
                 - field: logging.backends[0].type
                   message: 'unknown backend type. Available backends: "tcp", "file"'
                 - field: tracing.backends[0].type
-                  message: 'unknown backend type. Available backends: "zipkin"'
+                  message: 'unknown backend type. Available backends: "zipkin", "datadog"'
                 - field: metrics.backends[0].type
                   message: 'unknown backend type. Available backends: "prometheus"'`,
 			}),
diff --git a/pkg/kds/server/server_test.go b/pkg/kds/server/server_test.go
index 1ab266083..5dd3f342e 100644
--- a/pkg/kds/server/server_test.go
+++ b/pkg/kds/server/server_test.go
@@ -64,32 +64,38 @@ var _ = Describe("KDS Server", func() {
 		// Do not forget to update this test after updating 'kds.SupportedTypes
 		Expect([]proto.Message{
 			kds_samples.CircuitBreaker,
-			kds_samples.Ingress, // mesh.DataplaneType
-			kds_samples.ZoneIngress,
-			kds_samples.ZoneIngressInsight,
 			kds_samples.DataplaneInsight,
 			kds_samples.ExternalService,
 			kds_samples.FaultInjection,
+			kds_samples.GlobalSecret,
 			kds_samples.HealthCheck,
+			kds_samples.Ingress, // mesh.DataplaneType
 			kds_samples.Mesh1,
 			kds_samples.ProxyTemplate,
 			kds_samples.RateLimit,
 			kds_samples.Retry,
+			kds_samples.Secret,
 			kds_samples.Timeout,
 			kds_samples.TrafficLog,
 			kds_samples.TrafficPermission,
 			kds_samples.TrafficRoute,
 			kds_samples.TrafficTrace,
-			kds_samples.Secret,
-			kds_samples.GlobalSecret,
+			kds_samples.ZoneIngress,
+			kds_samples.ZoneIngressInsight,
 			kds_samples.Config,
 		}).
 			To(HaveLen(len(kds.SupportedTypes)))
 
 		vrf := kds_verifier.New().
+			// NOTE: The resources all have to be created before any DiscoveryRequests are made.
+			// This is because in the initial state, there are no snapshots. The first DiscoveryRequest
+			// won't get a response until the first snapshot is generated after the reconciliation
+			// period expires. Then all the subsequent DiscoveryRequests will be served from the
+			// same snapshot, so resources that aren't already present won't be reported.
+
 			Exec(kds_verifier.Create(ctx, &mesh.CircuitBreakerResource{Spec: kds_samples.CircuitBreaker}, store.CreateByKey("cb-1", "mesh-1"))).
-			Exec(kds_verifier.Create(ctx, &mesh.DataplaneResource{Spec: kds_samples.Ingress}, store.CreateByKey("Ingress-1", "mesh-1"))).
 			Exec(kds_verifier.Create(ctx, &mesh.DataplaneInsightResource{Spec: kds_samples.DataplaneInsight}, store.CreateByKey("insight-1", "mesh-1"))).
+			Exec(kds_verifier.Create(ctx, &mesh.DataplaneResource{Spec: kds_samples.Ingress}, store.CreateByKey("Ingress-1", "mesh-1"))).
 			Exec(kds_verifier.Create(ctx, &mesh.ExternalServiceResource{Spec: kds_samples.ExternalService}, store.CreateByKey("es-1", "mesh-1"))).
 			Exec(kds_verifier.Create(ctx, &mesh.FaultInjectionResource{Spec: kds_samples.FaultInjection}, store.CreateByKey("fi-1", "mesh-1"))).
 			Exec(kds_verifier.Create(ctx, &mesh.HealthCheckResource{Spec: kds_samples.HealthCheck}, store.CreateByKey("hc-1", "mesh-1"))).
@@ -272,10 +278,7 @@ var _ = Describe("KDS Server", func() {
 					return err
 				}
 				meshRes.Spec = kds_samples.Mesh2
-				if err := tc.Store().Update(ctx, meshRes); err != nil {
-					return err
-				}
-				return nil
+				return tc.Store().Update(ctx, meshRes)
 			}).
 			Exec(kds_verifier.WaitResponse(defaultTimeout, func(rs []model.Resource) {
 				Expect(rs).To(HaveLen(1))
diff --git a/pkg/xds/envoy/listeners/v3/tracing_configurer_test.go b/pkg/xds/envoy/listeners/v3/tracing_configurer_test.go
index 32baa628d..122f47f97 100644
--- a/pkg/xds/envoy/listeners/v3/tracing_configurer_test.go
+++ b/pkg/xds/envoy/listeners/v3/tracing_configurer_test.go
@@ -29,7 +29,7 @@ var _ = Describe("TracingConfigurer", func() {
 				Configure(InboundListener("inbound:192.168.0.1:8080", "192.168.0.1", 8080, xds.SocketAddressProtocolTCP)).
 				Configure(FilterChain(NewFilterChainBuilder(envoy.APIV3).
 					Configure(HttpConnectionManager("localhost:8080", false)).
-					Configure(Tracing(given.backend)))).
+					Configure(Tracing(given.backend, "service")))).
 				Build()
 			// then
 			Expect(err).ToNot(HaveOccurred())
@@ -40,7 +40,7 @@ var _ = Describe("TracingConfigurer", func() {
 			// and
 			Expect(actual).To(MatchYAML(given.expected))
 		},
-		Entry("backend specified with sampling", testCase{
+		Entry("zipkin backend specified with sampling", testCase{
 			backend: &mesh_proto.TracingBackend{
 				Name:     "zipkin",
 				Sampling: &wrapperspb.DoubleValue{Value: 30.5},
@@ -76,7 +76,7 @@ var _ = Describe("TracingConfigurer", func() {
             name: inbound:192.168.0.1:8080
             trafficDirection: INBOUND`,
 		}),
-		Entry("backend specified without sampling", testCase{
+		Entry("zipkin backend specified without sampling", testCase{
 			backend: &mesh_proto.TracingBackend{
 				Name: "zipkin",
 				Type: mesh_proto.TracingZipkinType,
@@ -109,6 +109,39 @@ var _ = Describe("TracingConfigurer", func() {
             name: inbound:192.168.0.1:8080
             trafficDirection: INBOUND`,
 		}),
+		Entry("datadog backend specified", testCase{
+			backend: &mesh_proto.TracingBackend{
+				Name: "datadog",
+				Type: mesh_proto.TracingDatadogType,
+				Conf: util_proto.MustToStruct(&mesh_proto.DatadogTracingBackendConfig{
+					Address: "1.1.1.1",
+					Port:    1111,
+				}),
+			},
+			expected: `
+        address:
+          socketAddress:
+            address: 192.168.0.1
+            portValue: 8080
+        filterChains:
+        - filters:
+          - name: envoy.filters.network.http_connection_manager
+            typedConfig:
+              '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
+              httpFilters:
+              - name: envoy.filters.http.router
+              statPrefix: localhost_8080
+              tracing:
+                provider:
+                  name: envoy.datadog
+                  typedConfig:
+                    '@type': type.googleapis.com/envoy.config.trace.v3.DatadogConfig
+                    collectorCluster: tracing:datadog
+                    serviceName: service
+        name: inbound:192.168.0.1:8080
+        trafficDirection: INBOUND`,
+		}),
+
 		Entry("no backend specified", testCase{
 			backend: nil,
 			expected: `
diff --git a/pkg/xds/server/callbacks/dataplane_callbacks_test.go b/pkg/xds/server/callbacks/dataplane_callbacks_test.go
new file mode 100644
index 000000000..536d59d2f
--- /dev/null
+++ b/pkg/xds/server/callbacks/dataplane_callbacks_test.go
@@ -0,0 +1,108 @@
+package callbacks_test
+
+import (
+	"context"
+
+	envoy_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
+	envoy_sd "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
+	"google.golang.org/protobuf/types/known/structpb"
+
+	core_xds "github.com/kumahq/kuma/pkg/core/xds"
+	util_xds_v3 "github.com/kumahq/kuma/pkg/util/xds/v3"
+
+	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
+	. "github.com/kumahq/kuma/pkg/xds/server/callbacks"
+)
+
+type countingDpCallbacks struct {
+	OnProxyConnectedCounter    int
+	OnProxyReconnectedCounter  int
+	OnProxyDisconnectedCounter int
+}
+
+func (c *countingDpCallbacks) OnProxyConnected(streamID core_xds.StreamID, dpKey core_model.ResourceKey, ctx context.Context, metadata core_xds.DataplaneMetadata) error {
+	c.OnProxyConnectedCounter++
+	return nil
+}
+
+func (c *countingDpCallbacks) OnProxyReconnected(streamID core_xds.StreamID, dpKey core_model.ResourceKey, ctx context.Context, metadata core_xds.DataplaneMetadata) error {
+	c.OnProxyReconnectedCounter++
+	return nil
+}
+
+func (c *countingDpCallbacks) OnProxyDisconnected(streamID core_xds.StreamID, dpKey core_model.ResourceKey) {
+	c.OnProxyDisconnectedCounter++
+}
+
+var _ DataplaneCallbacks = &countingDpCallbacks{}
+
+var _ = Describe("Dataplane Callbacks", func() {
+
+	countingCallbacks := &countingDpCallbacks{}
+	callbacks := util_xds_v3.AdaptCallbacks(DataplaneCallbacksToXdsCallbacks(countingCallbacks))
+
+	req := envoy_sd.DiscoveryRequest{
+		Node: &envoy_core.Node{
+			Id: "default.example",
+			Metadata: &structpb.Struct{
+				Fields: map[string]*structpb.Value{
+					"dataplane.token": &structpb.Value{
+						Kind: &structpb.Value_StringValue{
+							StringValue: "token",
+						},
+					},
+				},
+			},
+		},
+	}
+
+	It("should call DataplaneCallbacks correctly", func() {
+		// when only OnStreamOpen is called
+		err := callbacks.OnStreamOpen(context.Background(), 1, "")
+		Expect(err).ToNot(HaveOccurred())
+
+		// then OnProxyConnected and OnProxyReconnected is not yet called
+		Expect(countingCallbacks.OnProxyConnectedCounter).To(Equal(0))
+		Expect(countingCallbacks.OnProxyReconnectedCounter).To(Equal(0))
+
+		// when OnStreamRequest is sent
+		err = callbacks.OnStreamRequest(1, &req)
+		Expect(err).ToNot(HaveOccurred())
+
+		// then only OnProxyConnected should be called
+		Expect(countingCallbacks.OnProxyConnectedCounter).To(Equal(1))
+		Expect(countingCallbacks.OnProxyReconnectedCounter).To(Equal(0))
+
+		// when next OnStreamRequest on the same stream is sent
+		err = callbacks.OnStreamRequest(1, &req)
+		Expect(err).ToNot(HaveOccurred())
+
+		// then OnProxyReconnected and OnProxyReconnected are not called again, they should be only called on the first DiscoveryRequest
+		Expect(countingCallbacks.OnProxyConnectedCounter).To(Equal(1))
+		Expect(countingCallbacks.OnProxyReconnectedCounter).To(Equal(0))
+
+		// when next stream for given data plane proxy is connected
+		err = callbacks.OnStreamOpen(context.Background(), 2, "")
+		Expect(err).ToNot(HaveOccurred())
+		err = callbacks.OnStreamRequest(2, &req)
+		Expect(err).ToNot(HaveOccurred())
+
+		// then only OnProxyReconnected should be called
+		Expect(countingCallbacks.OnProxyConnectedCounter).To(Equal(1))
+		Expect(countingCallbacks.OnProxyReconnectedCounter).To(Equal(1))
+
+		// when first stream is closed
+		callbacks.OnStreamClosed(1)
+
+		// then OnProxyDisconnected should not yet be called
+		Expect(countingCallbacks.OnProxyDisconnectedCounter).To(Equal(0))
+
+		// when last stream is closed
+		callbacks.OnStreamClosed(2)
+
+		// then OnProxyDisconnected should be called
+		Expect(countingCallbacks.OnProxyDisconnectedCounter).To(Equal(1))
+	})
+})
diff --git a/pkg/xds/server/callbacks/dataplane_lifecycle_test.go b/pkg/xds/server/callbacks/dataplane_lifecycle_test.go
index 42ff50a84..58b68ec1e 100644
--- a/pkg/xds/server/callbacks/dataplane_lifecycle_test.go
+++ b/pkg/xds/server/callbacks/dataplane_lifecycle_test.go
@@ -24,7 +24,6 @@ import (
 var _ = Describe("Dataplane Lifecycle", func() {
 
 	var resManager core_manager.ResourceManager
-	var dpLifecycle *DataplaneLifecycle
 	var callbacks envoy_server.Callbacks
 	var shutdown chan struct{}
 
@@ -32,8 +31,7 @@ var _ = Describe("Dataplane Lifecycle", func() {
 		store := memory.NewStore()
 		resManager = core_manager.NewResourceManager(store)
 		shutdown = make(chan struct{})
-		dpLifecycle = NewDataplaneLifecycle(resManager, shutdown)
-		callbacks = util_xds_v2.AdaptCallbacks(dpLifecycle)
+		callbacks = util_xds_v2.AdaptCallbacks(DataplaneCallbacksToXdsCallbacks(NewDataplaneLifecycle(resManager, shutdown)))
 
 		err := resManager.Create(context.Background(), core_mesh.NewMeshResource(), core_store.CreateByKey(core_model.DefaultMesh, core_model.NoMesh))
 		Expect(err).ToNot(HaveOccurred())
@@ -84,7 +82,7 @@ var _ = Describe("Dataplane Lifecycle", func() {
 		Expect(err).ToNot(HaveOccurred())
 
 		// when
-		dpLifecycle.OnStreamClosed(streamId)
+		callbacks.OnStreamClosed(streamId)
 
 		// then dataplane should be deleted
 		err = resManager.Get(context.Background(), core_mesh.NewDataplaneResource(), core_store.GetByKey("backend-01", "default"))
@@ -130,7 +128,7 @@ var _ = Describe("Dataplane Lifecycle", func() {
 		Expect(err).ToNot(HaveOccurred())
 
 		// when
-		dpLifecycle.OnStreamClosed(streamId)
+		callbacks.OnStreamClosed(streamId)
 
 		// then DP is not deleted because it was not carried in metadata
 		err = resManager.Get(context.Background(), core_mesh.NewDataplaneResource(), core_store.GetByKey("backend-01", "default"))
@@ -180,7 +178,7 @@ var _ = Describe("Dataplane Lifecycle", func() {
 
 		close(shutdown)
 		// when
-		dpLifecycle.OnStreamClosed(streamId)
+		callbacks.OnStreamClosed(streamId)
 
 		// then DP is not deleted because Kuma CP was shutting down
 		err = resManager.Get(context.Background(), core_mesh.NewDataplaneResource(), core_store.GetByKey("backend-01", "default"))
diff --git a/pkg/xds/server/callbacks/dataplane_metadata_tracker_test.go b/pkg/xds/server/callbacks/dataplane_metadata_tracker_test.go
index 6d11c9739..9df3d67eb 100644
--- a/pkg/xds/server/callbacks/dataplane_metadata_tracker_test.go
+++ b/pkg/xds/server/callbacks/dataplane_metadata_tracker_test.go
@@ -7,7 +7,7 @@ import (
 	. "github.com/onsi/gomega"
 	"google.golang.org/protobuf/types/known/structpb"
 
-	"github.com/kumahq/kuma/pkg/core/xds"
+	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	util_xds_v2 "github.com/kumahq/kuma/pkg/util/xds/v2"
 	. "github.com/kumahq/kuma/pkg/xds/server/callbacks"
 )
@@ -15,8 +15,12 @@ import (
 var _ = Describe("Dataplane Metadata Tracker", func() {
 
 	tracker := NewDataplaneMetadataTracker()
-	callbacks := util_xds_v2.AdaptCallbacks(tracker)
+	callbacks := util_xds_v2.AdaptCallbacks(DataplaneCallbacksToXdsCallbacks(tracker))
 
+	dpKey := core_model.ResourceKey{
+		Mesh: "default",
+		Name: "example",
+	}
 	req := v2.DiscoveryRequest{
 		Node: &envoy_core.Node{
 			Id: "default.example",
@@ -41,17 +45,17 @@ var _ = Describe("Dataplane Metadata Tracker", func() {
 		Expect(err).ToNot(HaveOccurred())
 
 		// when
-		metadata := tracker.Metadata(streamId)
+		metadata := tracker.Metadata(dpKey)
 
 		// then
 		Expect(metadata.GetDataplaneTokenPath()).To(Equal("/tmp/token"))
 
 		// when
-		tracker.OnStreamClosed(streamId)
+		callbacks.OnStreamClosed(streamId)
 
 		// then metadata should be deleted
-		metadata = tracker.Metadata(streamId)
-		Expect(metadata).To(Equal(&xds.DataplaneMetadata{}))
+		metadata = tracker.Metadata(dpKey)
+		Expect(metadata).To(BeNil())
 	})
 
 	It("should track metadata with empty Node in consecutive DiscoveryRequests", func() {
@@ -68,7 +72,7 @@ var _ = Describe("Dataplane Metadata Tracker", func() {
 		Expect(err).ToNot(HaveOccurred())
 
 		// when
-		metadata := tracker.Metadata(streamId)
+		metadata := tracker.Metadata(dpKey)
 
 		// then
 		Expect(metadata.GetDataplaneTokenPath()).To(Equal("/tmp/token"))
diff --git a/pkg/xds/server/callbacks/dataplane_sync_tracker_test.go b/pkg/xds/server/callbacks/dataplane_sync_tracker_test.go
index c179aebf9..1b3819e49 100644
--- a/pkg/xds/server/callbacks/dataplane_sync_tracker_test.go
+++ b/pkg/xds/server/callbacks/dataplane_sync_tracker_test.go
@@ -8,8 +8,6 @@ import (
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
 
-	core_xds "github.com/kumahq/kuma/pkg/core/xds"
-
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	"github.com/kumahq/kuma/pkg/test"
 	util_watchdog "github.com/kumahq/kuma/pkg/util/watchdog"
@@ -25,7 +23,7 @@ var _ = Describe("Sync", func() {
 	Describe("dataplaneSyncTracker", func() {
 		It("should not fail when ADS stream is closed before Watchdog is even created", func() {
 			// setup
-			tracker := NewDataplaneSyncTracker(nil)
+			tracker := DataplaneCallbacksToXdsCallbacks(NewDataplaneSyncTracker(nil))
 
 			// given
 			ctx := context.Background()
@@ -49,7 +47,7 @@ var _ = Describe("Sync", func() {
 		It("should not fail when Envoy presents invalid Node ID", func() {
 			// setup
 			tracker := NewDataplaneSyncTracker(nil)
-			callbacks := util_xds_v2.AdaptCallbacks(tracker)
+			callbacks := util_xds_v2.AdaptCallbacks(DataplaneCallbacksToXdsCallbacks(tracker))
 
 			// given
 			ctx := context.Background()
@@ -81,14 +79,14 @@ var _ = Describe("Sync", func() {
 			watchdogCh := make(chan core_model.ResourceKey)
 
 			// setup
-			tracker := NewDataplaneSyncTracker(func(proxyId *core_xds.ProxyId, streamId int64) util_watchdog.Watchdog {
+			tracker := NewDataplaneSyncTracker(func(key core_model.ResourceKey) util_watchdog.Watchdog {
 				return WatchdogFunc(func(stop <-chan struct{}) {
-					watchdogCh <- proxyId.ToResourceKey()
+					watchdogCh <- key
 					<-stop
 					close(watchdogCh)
 				})
 			})
-			callbacks := util_xds_v2.AdaptCallbacks(tracker)
+			callbacks := util_xds_v2.AdaptCallbacks(DataplaneCallbacksToXdsCallbacks(tracker))
 
 			// given
 			ctx := context.Background()
@@ -138,14 +136,14 @@ var _ = Describe("Sync", func() {
 		It("should start only one watchdog per dataplane", func() {
 			// setup
 			var activeWatchdogs int32
-			tracker := NewDataplaneSyncTracker(func(proxyId *core_xds.ProxyId, streamId int64) util_watchdog.Watchdog {
+			tracker := NewDataplaneSyncTracker(func(key core_model.ResourceKey) util_watchdog.Watchdog {
 				return WatchdogFunc(func(stop <-chan struct{}) {
 					atomic.AddInt32(&activeWatchdogs, 1)
 					<-stop
 					atomic.AddInt32(&activeWatchdogs, -1)
 				})
 			})
-			callbacks := util_xds_v2.AdaptCallbacks(tracker)
+			callbacks := util_xds_v2.AdaptCallbacks(DataplaneCallbacksToXdsCallbacks(tracker))
 
 			// when one stream for backend-01 is connected and request is sent
 			streamID := int64(1)