Repository: kumahq/kuma
Pull request #2511 (2021-08-06 11:52:56)

chore(tools): Simplify resource-gen.go by generating`ResourceDescriptor`
This descriptor defines everything there is about a resource:

- kds
- kumactl
- ws

We then simplify a lot of the redundant code to use this descriptor.
While refactoring some of the deep references to the global registry were removed

lahabana: @jpeach I think this is the last one :) It also adds the kumactl for gateway for free!

I need to add my virtual-outbound policy after so I'll be able to see how this works out.
I can guess some tests will maybe be deleted because they were there to ensure that manual code additions were done which are automatic now.
codecov-commenter: # [Codecov](https://codecov.io/gh/kumahq/kuma/pull/2511?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq) Report
> Merging [#2511](https://codecov.io/gh/kumahq/kuma/pull/2511?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq) (95a0343) into [master](https://codecov.io/gh/kumahq/kuma/commit/686935a133fe521dc325f39419cc32a11f11c882?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq) (686935a) will **increase** coverage by `0.30%`.
> The diff coverage is `92.80%`.

[![Impacted file tree graph](https://codecov.io/gh/kumahq/kuma/pull/2511/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/2511?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq)

```diff
@@            Coverage Diff             @@
##           master    #2511      +/-   ##
==========================================
+ Coverage   52.19%   52.50%   +0.30%     
==========================================
  Files         866      864       -2     
  Lines       48304    48568     +264     
==========================================
+ Hits        25214    25499     +285     
+ Misses      21050    21035      -15     
+ Partials     2040     2034       -6     
```


| [Impacted Files](https://codecov.io/gh/kumahq/kuma/pull/2511?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq) | Coverage Δ | |
|---|---|---|
| [api/mesh/v1alpha1/gateway.pb.go](https://codecov.io/gh/kumahq/kuma/pull/2511/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq#diff-YXBpL21lc2gvdjFhbHBoYTEvZ2F0ZXdheS5wYi5nbw==) | `33.57% <ø> (ø)` | |
| [pkg/insights/components.go](https://codecov.io/gh/kumahq/kuma/pull/2511/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq#diff-cGtnL2luc2lnaHRzL2NvbXBvbmVudHMuZ28=) | `100.00% <ø> (ø)` | |
| [pkg/kds/global/components.go](https://codecov.io/gh/kumahq/kuma/pull/2511/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq#diff-cGtnL2tkcy9nbG9iYWwvY29tcG9uZW50cy5nbw==) | `12.96% <0.00%> (-0.25%)` | :arrow_down: |
| [pkg/kds/zone/components.go](https://codecov.io/gh/kumahq/kuma/pull/2511/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq#diff-cGtnL2tkcy96b25lL2NvbXBvbmVudHMuZ28=) | `18.96% <0.00%> (-0.34%)` | :arrow_down: |
| [pkg/plugins/runtime/gateway/kumactl/plugin.go](https://codecov.io/gh/kumahq/kuma/pull/2511/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq#diff-cGtnL3BsdWdpbnMvcnVudGltZS9nYXRld2F5L2t1bWFjdGwvcGx1Z2luLmdv) | `0.00% <0.00%> (ø)` | |
| [tools/resource-gen.go](https://codecov.io/gh/kumahq/kuma/pull/2511/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq#diff-dG9vbHMvcmVzb3VyY2UtZ2VuLmdv) | `0.00% <0.00%> (ø)` | |
| [app/kumactl/pkg/cmd/root\_context.go](https://codecov.io/gh/kumahq/kuma/pull/2511/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq#diff-YXBwL2t1bWFjdGwvcGtnL2NtZC9yb290X2NvbnRleHQuZ28=) | `70.73% <40.00%> (-6.93%)` | :arrow_down: |
| [pkg/plugins/runtime/k8s/webhooks/validation.go](https://codecov.io/gh/kumahq/kuma/pull/2511/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq#diff-cGtnL3BsdWdpbnMvcnVudGltZS9rOHMvd2ViaG9va3MvdmFsaWRhdGlvbi5nbw==) | `87.39% <60.00%> (-1.87%)` | :arrow_down: |
| [pkg/core/resources/model/rest/api.go](https://codecov.io/gh/kumahq/kuma/pull/2511/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq#diff-cGtnL2NvcmUvcmVzb3VyY2VzL21vZGVsL3Jlc3QvYXBpLmdv) | `80.95% <66.66%> (-8.53%)` | :arrow_down: |
| [app/kumactl/pkg/resources/store.go](https://codecov.io/gh/kumahq/kuma/pull/2511/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq#diff-YXBwL2t1bWFjdGwvcGtnL3Jlc291cmNlcy9zdG9yZS5nbw==) | `80.00% <71.42%> (-20.00%)` | :arrow_down: |
| ... and [27 more](https://codecov.io/gh/kumahq/kuma/pull/2511/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/kumahq/kuma/pull/2511?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/2511?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kumahq). Last update [686935a...95a0343](https://codecov.io/gh/kumahq/kuma/pull/2511?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).


Patch:
diff --git a/api/mesh/v1alpha1/gateway.pb.go b/api/mesh/v1alpha1/gateway.pb.go
index 08c662762..f942b26c3 100644
--- a/api/mesh/v1alpha1/gateway.pb.go
+++ b/api/mesh/v1alpha1/gateway.pb.go
@@ -527,7 +527,7 @@ var file_mesh_v1alpha1_gateway_proto_rawDesc = []byte{
 	0x6f, 0x74, 0x6f, 0x1a, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c,
 	0x70, 0x68, 0x61, 0x31, 0x2f, 0x64, 0x61, 0x74, 0x61, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 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, 0xb4,
+	0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbe,
 	0x08, 0x0a, 0x07, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x12, 0x40, 0x0a, 0x07, 0x73, 0x6f,
 	0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6b, 0x75,
 	0x6d, 0x61, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
@@ -590,15 +590,16 @@ var file_mesh_v1alpha1_gateway_proto_rawDesc = []byte{
 	0x37, 0x0a, 0x09, 0x54, 0x61, 0x67, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03,
 	0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14,
 	0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76,
-	0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x4b, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x11,
+	0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x3a, 0x55, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x11,
 	0x0a, 0x0f, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
 	0x65, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x09, 0x12, 0x07, 0x47, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79,
 	0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x06, 0x22, 0x04, 0x6d, 0x65, 0x73, 0x68, 0xaa, 0x8c, 0x89, 0xa6,
-	0x01, 0x02, 0x30, 0x01, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x0b, 0x3a, 0x09, 0x0a, 0x07, 0x67, 0x61,
-	0x74, 0x65, 0x77, 0x61, 0x79, 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,
+	0x01, 0x02, 0x30, 0x01, 0xaa, 0x8c, 0x89, 0xa6, 0x01, 0x04, 0x52, 0x02, 0x08, 0x01, 0xaa, 0x8c,
+	0x89, 0xa6, 0x01, 0x0b, 0x3a, 0x09, 0x0a, 0x07, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0x79, 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 (
diff --git a/api/mesh/v1alpha1/gateway.proto b/api/mesh/v1alpha1/gateway.proto
index 7851a2bb9..53357e790 100644
--- a/api/mesh/v1alpha1/gateway.proto
+++ b/api/mesh/v1alpha1/gateway.proto
@@ -24,6 +24,7 @@ message Gateway {
   option (kuma.mesh.resource).package = "mesh";
 
   option (kuma.mesh.resource).skip_registration = true;
+  option (kuma.mesh.resource).kds.send_to_global = true;
   option (kuma.mesh.resource).ws.name = "gateway";
 
   // TLSConfig describes a TLS configuration.
diff --git a/app/kuma-dp/cmd/dataplane.go b/app/kuma-dp/cmd/dataplane.go
index d2525c9d6..76d8f1431 100644
--- a/app/kuma-dp/cmd/dataplane.go
+++ b/app/kuma-dp/cmd/dataplane.go
@@ -45,7 +45,7 @@ func readResource(cmd *cobra.Command, r *kuma_dp.DataplaneRuntime) (model.Resour
 		return nil, err
 	}
 
-	if err := core_mesh.ValidateMeta(res.GetMeta().GetName(), res.GetMeta().GetMesh(), res.Scope()); err.HasViolations() {
+	if err := core_mesh.ValidateMeta(res.GetMeta().GetName(), res.GetMeta().GetMesh(), res.Descriptor().Scope); err.HasViolations() {
 		return nil, &err
 	}
 
diff --git a/app/kuma-dp/cmd/run.go b/app/kuma-dp/cmd/run.go
index 109474fa5..eebce0258 100644
--- a/app/kuma-dp/cmd/run.go
+++ b/app/kuma-dp/cmd/run.go
@@ -77,9 +77,9 @@ func newRunCmd(rootCtx *RootContext) *cobra.Command {
 			}
 
 			if proxyResource != nil {
-				if resType := proxyTypeMap[cfg.Dataplane.ProxyType]; resType != proxyResource.GetType() {
+				if resType := proxyTypeMap[cfg.Dataplane.ProxyType]; resType != proxyResource.Descriptor().Name {
 					return errors.Errorf("invalid proxy resource type %q, expected %s",
-						proxyResource.GetType(), resType)
+						proxyResource.Descriptor().Name, resType)
 				}
 
 				if cfg.Dataplane.Name != "" || cfg.Dataplane.Mesh != "" {
diff --git a/app/kumactl/cmd/apply/apply.go b/app/kumactl/cmd/apply/apply.go
index b60b34034..d0a7d210c 100644
--- a/app/kumactl/cmd/apply/apply.go
+++ b/app/kumactl/cmd/apply/apply.go
@@ -108,7 +108,7 @@ $ kumactl apply -f https://example.com/resource.yaml
 				if err != nil {
 					return errors.Wrap(err, "YAML contains invalid resource")
 				}
-				if err := mesh.ValidateMeta(res.GetMeta().GetName(), res.GetMeta().GetMesh(), res.Scope()); err.HasViolations() {
+				if err := mesh.ValidateMeta(res.GetMeta().GetName(), res.GetMeta().GetMesh(), res.Descriptor().Scope); err.HasViolations() {
 					return err.OrNil()
 				}
 				resources = append(resources, res)
@@ -128,7 +128,7 @@ $ kumactl apply -f https://example.com/resource.yaml
 						return err
 					}
 
-					if err := upsert(rs, resource); err != nil {
+					if err := upsert(pctx.Runtime.Registry, rs, resource); err != nil {
 						return err
 					}
 				}
@@ -143,8 +143,8 @@ $ kumactl apply -f https://example.com/resource.yaml
 	return cmd
 }
 
-func upsert(rs store.ResourceStore, res model.Resource) error {
-	newRes, err := registry.Global().NewObject(res.GetType())
+func upsert(typeRegistry registry.TypeRegistry, rs store.ResourceStore, res model.Resource) error {
+	newRes, err := typeRegistry.NewObject(res.Descriptor().Name)
 	if err != nil {
 		return err
 	}
diff --git a/app/kumactl/cmd/delete/delete.go b/app/kumactl/cmd/delete/delete.go
index 2ab7b2dea..1a8ec4a16 100644
--- a/app/kumactl/cmd/delete/delete.go
+++ b/app/kumactl/cmd/delete/delete.go
@@ -2,19 +2,25 @@ package delete
 
 import (
 	"context"
+	"sort"
 	"strings"
 
 	"github.com/pkg/errors"
 	"github.com/spf13/cobra"
 
 	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
-	"github.com/kumahq/kuma/app/kumactl/pkg/entities"
 	"github.com/kumahq/kuma/pkg/core/resources/model"
-	"github.com/kumahq/kuma/pkg/core/resources/registry"
 	"github.com/kumahq/kuma/pkg/core/resources/store"
 )
 
 func NewDeleteCmd(pctx *kumactl_cmd.RootContext) *cobra.Command {
+	byName := map[string]model.ResourceTypeDescriptor{}
+	allNames := []string{}
+	for _, desc := range pctx.Runtime.Registry.ObjectDescriptors(model.HasKumactlEnabled()) {
+		byName[desc.KumactlArg] = desc
+		allNames = append(allNames, desc.KumactlArg)
+	}
+	sort.Strings(allNames)
 	cmd := &cobra.Command{
 		Use:   "delete TYPE NAME",
 		Short: "Delete Kuma resources",
@@ -24,11 +30,11 @@ func NewDeleteCmd(pctx *kumactl_cmd.RootContext) *cobra.Command {
 			resourceTypeArg := args[0]
 			name := args[1]
 
-			def, ok := entities.ByName[resourceTypeArg]
+			desc, ok := byName[resourceTypeArg]
 			if !ok {
-				return errors.Errorf("unknown TYPE: %s. Allowed values: %s", resourceTypeArg, strings.Join(entities.Names, ", "))
+				return errors.Errorf("unknown TYPE: %s. Allowed values: %s", resourceTypeArg, strings.Join(allNames, ", "))
 			}
-			if def.ReadOnly {
+			if desc.ReadOnly {
 				return errors.Errorf("TYPE: %s is readOnly, can't use it for write action", resourceTypeArg)
 			}
 
@@ -37,21 +43,16 @@ func NewDeleteCmd(pctx *kumactl_cmd.RootContext) *cobra.Command {
 				return err
 			}
 
-			var resource model.Resource
-			if resource, err = registry.Global().NewObject(def.ResourceType); err != nil {
-				return err
-			}
-
 			mesh := model.NoMesh
-			if resource.Scope() == model.ScopeMesh {
+			if desc.Scope == model.ScopeMesh {
 				mesh = pctx.CurrentMesh()
 			}
 
-			if err := deleteResource(name, mesh, resource, def.ResourceType, rs); err != nil {
+			if err := deleteResource(name, mesh, desc, rs); err != nil {
 				return err
 			}
 
-			cmd.Printf("deleted %s %q\n", def.ResourceType, name)
+			cmd.Printf("deleted %s %q\n", desc.Name, name)
 			return nil
 		},
 	}
@@ -59,13 +60,14 @@ func NewDeleteCmd(pctx *kumactl_cmd.RootContext) *cobra.Command {
 	return cmd
 }
 
-func deleteResource(name string, mesh string, resource model.Resource, resourceType model.ResourceType, rs store.ResourceStore) error {
+func deleteResource(name string, mesh string, desc model.ResourceTypeDescriptor, rs store.ResourceStore) error {
+	resource := desc.NewObject()
 	deleteOptions := store.DeleteBy(model.ResourceKey{Mesh: mesh, Name: name})
 	if err := rs.Delete(context.Background(), resource, deleteOptions); err != nil {
 		if store.IsResourceNotFound(err) {
-			return errors.Errorf("there is no %s with name %q", resourceType, name)
+			return errors.Errorf("there is no %s with name %q", desc.Name, name)
 		}
-		return errors.Wrapf(err, "failed to delete %s with the name %q", resourceType, name)
+		return errors.Wrapf(err, "failed to delete %s with the name %q", desc.Name, name)
 	}
 
 	return nil
diff --git a/app/kumactl/cmd/get/get.go b/app/kumactl/cmd/get/get.go
index 2418f9c98..29766fdc7 100644
--- a/app/kumactl/cmd/get/get.go
+++ b/app/kumactl/cmd/get/get.go
@@ -5,9 +5,9 @@ import (
 
 	get_context "github.com/kumahq/kuma/app/kumactl/cmd/get/context"
 	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
-	"github.com/kumahq/kuma/app/kumactl/pkg/entities"
 	"github.com/kumahq/kuma/app/kumactl/pkg/output"
 	kuma_cmd "github.com/kumahq/kuma/pkg/cmd"
+	"github.com/kumahq/kuma/pkg/core/resources/model"
 )
 
 func NewGetCmd(pctx *kumactl_cmd.RootContext) *cobra.Command {
@@ -18,9 +18,9 @@ func NewGetCmd(pctx *kumactl_cmd.RootContext) *cobra.Command {
 	}
 	// flags
 	cmd.PersistentFlags().StringVarP(&pctx.GetContext.Args.OutputFormat, "output", "o", string(output.TableFormat), kuma_cmd.UsageOptions("output format", output.TableFormat, output.YAMLFormat, output.JSONFormat))
-	for _, cmdInst := range entities.All {
-		cmd.AddCommand(WithPaginationArgs(NewGetResourcesCmd(pctx, cmdInst.Plural, cmdInst.ResourceType), &pctx.ListContext))
-		cmd.AddCommand(NewGetResourceCmd(pctx, cmdInst.Singular, cmdInst.ResourceType))
+	for _, cmdInst := range pctx.Runtime.Registry.ObjectDescriptors(model.HasKumactlEnabled()) {
+		cmd.AddCommand(WithPaginationArgs(NewGetResourcesCmd(pctx, cmdInst), &pctx.ListContext))
+		cmd.AddCommand(NewGetResourceCmd(pctx, cmdInst))
 	}
 	return cmd
 }
diff --git a/app/kumactl/cmd/get/get_resource.go b/app/kumactl/cmd/get/get_resource.go
index d6d434d1f..e0fbc7654 100644
--- a/app/kumactl/cmd/get/get_resource.go
+++ b/app/kumactl/cmd/get/get_resource.go
@@ -12,15 +12,14 @@ import (
 	"github.com/kumahq/kuma/app/kumactl/pkg/output/printers"
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	rest_types "github.com/kumahq/kuma/pkg/core/resources/model/rest"
-	"github.com/kumahq/kuma/pkg/core/resources/registry"
 	"github.com/kumahq/kuma/pkg/core/resources/store"
 )
 
-func NewGetResourceCmd(pctx *kumactl_cmd.RootContext, use string, resourceType core_model.ResourceType) *cobra.Command {
+func NewGetResourceCmd(pctx *kumactl_cmd.RootContext, desc core_model.ResourceTypeDescriptor) *cobra.Command {
 	cmd := &cobra.Command{
-		Use:   fmt.Sprintf("%s NAME", use),
-		Short: fmt.Sprintf("Show a single %s resource", resourceType),
-		Long:  fmt.Sprintf("Show a single %s resource.", resourceType),
+		Use:   fmt.Sprintf("%s NAME", desc.KumactlArg),
+		Short: fmt.Sprintf("Show a single %s resource", desc.Name),
+		Long:  fmt.Sprintf("Show a single %s resource.", desc.Name),
 		Args:  cobra.MinimumNArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
 			rs, err := pctx.CurrentResourceStore()
@@ -29,19 +28,16 @@ func NewGetResourceCmd(pctx *kumactl_cmd.RootContext, use string, resourceType c
 			}
 			name := args[0]
 
-			resource, err := registry.Global().NewObject(resourceType)
-			if err != nil {
-				return err
-			}
-
-			if resource.Scope() == core_model.ScopeGlobal {
+			resource := desc.NewObject()
+			switch desc.Scope {
+			case core_model.ScopeGlobal:
 				if err := rs.Get(context.Background(), resource, store.GetByKey(name, "")); err != nil {
 					if store.IsResourceNotFound(err) {
 						return errors.New("No resources found")
 					}
 					return errors.Wrapf(err, "failed to get %s", name)
 				}
-			} else {
+			case core_model.ScopeMesh:
 				currentMesh := pctx.CurrentMesh()
 				if err := rs.Get(context.Background(), resource, store.GetByKey(name, currentMesh)); err != nil {
 					if store.IsResourceNotFound(err) {
@@ -49,19 +45,18 @@ func NewGetResourceCmd(pctx *kumactl_cmd.RootContext, use string, resourceType c
 					}
 					return errors.Wrapf(err, "failed to get %s in mesh %s", name, currentMesh)
 				}
+			default:
+				return fmt.Errorf("Scope %s is unsupported", desc.Scope)
 			}
 
-			resources, err := registry.Global().NewList(resourceType)
-			if err != nil {
-				return err
-			}
+			resources := desc.NewList()
 			if err := resources.AddItem(resource); err != nil {
 				return err
 			}
 
 			switch format := output.Format(pctx.GetContext.Args.OutputFormat); format {
 			case output.TableFormat:
-				return ResolvePrinter(resourceType, resource.Scope()).Print(pctx.Now(), resources, cmd.OutOrStdout())
+				return ResolvePrinter(desc.Name, resource.Descriptor().Scope).Print(pctx.Now(), resources, cmd.OutOrStdout())
 			default:
 				printer, err := printers.NewGenericPrinter(format)
 				if err != nil {
diff --git a/app/kumactl/cmd/get/get_resources.go b/app/kumactl/cmd/get/get_resources.go
index 7ba3d0386..5d348d70d 100644
--- a/app/kumactl/cmd/get/get_resources.go
+++ b/app/kumactl/cmd/get/get_resources.go
@@ -12,37 +12,33 @@ import (
 	"github.com/kumahq/kuma/app/kumactl/pkg/output/printers"
 	"github.com/kumahq/kuma/pkg/core/resources/model"
 	rest_types "github.com/kumahq/kuma/pkg/core/resources/model/rest"
-	"github.com/kumahq/kuma/pkg/core/resources/registry"
 	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
 )
 
-func NewGetResourcesCmd(pctx *kumactl_cmd.RootContext, use string, resourceType model.ResourceType) *cobra.Command {
+func NewGetResourcesCmd(pctx *kumactl_cmd.RootContext, desc model.ResourceTypeDescriptor) *cobra.Command {
 	cmd := &cobra.Command{
-		Use:   use,
-		Short: fmt.Sprintf("Show %s", resourceType),
-		Long:  fmt.Sprintf("Show %s entities.", resourceType),
+		Use:   desc.KumactlListArg,
+		Short: fmt.Sprintf("Show %s", desc.Name),
+		Long:  fmt.Sprintf("Show %s entities.", desc.Name),
 		RunE: func(cmd *cobra.Command, _ []string) error {
 			rs, err := pctx.CurrentResourceStore()
 			if err != nil {
 				return err
 			}
 
-			resources, err := registry.Global().NewList(resourceType)
-			if err != nil {
-				return err
-			}
+			resources := desc.NewList()
 			currentMesh := pctx.CurrentMesh()
 			resource := resources.NewItem()
-			if resource.Scope() == model.ScopeGlobal {
+			if resource.Descriptor().Scope == model.ScopeGlobal {
 				currentMesh = ""
 			}
 			if err := rs.List(context.Background(), resources, core_store.ListByMesh(currentMesh), core_store.ListByPage(pctx.ListContext.Args.Size, pctx.ListContext.Args.Offset)); err != nil {
-				return errors.Wrapf(err, "failed to list "+string(resourceType))
+				return errors.Wrapf(err, "failed to list "+string(desc.Name))
 			}
 
 			switch format := output.Format(pctx.GetContext.Args.OutputFormat); format {
 			case output.TableFormat:
-				return ResolvePrinter(resourceType, resource.Scope()).Print(pctx.Now(), resources, cmd.OutOrStdout())
+				return ResolvePrinter(desc.Name, resource.Descriptor().Scope).Print(pctx.Now(), resources, cmd.OutOrStdout())
 			default:
 				printer, err := printers.NewGenericPrinter(format)
 				if err != nil {
diff --git a/app/kumactl/pkg/cmd/root_context.go b/app/kumactl/pkg/cmd/root_context.go
index 898c3e1f0..e7670728c 100644
--- a/app/kumactl/pkg/cmd/root_context.go
+++ b/app/kumactl/pkg/cmd/root_context.go
@@ -13,6 +13,7 @@ import (
 	"github.com/kumahq/kuma/app/kumactl/pkg/tokens"
 	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
+	"github.com/kumahq/kuma/pkg/core/resources/registry"
 	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
 	util_files "github.com/kumahq/kuma/pkg/util/files"
 )
@@ -33,6 +34,7 @@ type RootRuntime struct {
 	NewDataplaneTokenClient      func(*config_proto.ControlPlaneCoordinates_ApiServer) (tokens.DataplaneTokenClient, error)
 	NewZoneIngressTokenClient    func(*config_proto.ControlPlaneCoordinates_ApiServer) (tokens.ZoneIngressTokenClient, error)
 	NewAPIServerClient           func(*config_proto.ControlPlaneCoordinates_ApiServer) (kumactl_resources.ApiServerClient, error)
+	Registry                     registry.TypeRegistry
 }
 
 // RootContext contains variables, functions and components that can be overridden when extending kumactl or running the test.
@@ -60,8 +62,11 @@ type RootContext struct {
 func DefaultRootContext() *RootContext {
 	return &RootContext{
 		Runtime: RootRuntime{
-			Now:                          time.Now,
-			NewResourceStore:             kumactl_resources.NewResourceStore,
+			Now:      time.Now,
+			Registry: registry.Global(),
+			NewResourceStore: func(server *config_proto.ControlPlaneCoordinates_ApiServer) (core_store.ResourceStore, error) {
+				return kumactl_resources.NewResourceStore(server, registry.Global().ObjectDescriptors())
+			},
 			NewDataplaneOverviewClient:   kumactl_resources.NewDataplaneOverviewClient,
 			NewZoneIngressOverviewClient: kumactl_resources.NewZoneIngressOverviewClient,
 			NewZoneOverviewClient:        kumactl_resources.NewZoneOverviewClient,
diff --git a/app/kumactl/pkg/entities/entities.go b/app/kumactl/pkg/entities/entities.go
deleted file mode 100644
index c11d3b6b0..000000000
--- a/app/kumactl/pkg/entities/entities.go
+++ /dev/null
@@ -1,33 +0,0 @@
-package entities
-
-import (
-	core_system "github.com/kumahq/kuma/pkg/core/resources/apis/system"
-	"github.com/kumahq/kuma/pkg/core/resources/model"
-)
-
-type Definition struct {
-	Singular     string
-	Plural       string
-	ResourceType model.ResourceType
-	ReadOnly     bool
-}
-
-var All []Definition
-var Names []string
-var ByName = map[string]Definition{}
-
-func init() {
-	All = append(All, meshEntities...)
-	All = append(All, systemEntities...)
-	All = append(All, Definition{Singular: "global-secret", Plural: "global-secrets", ResourceType: core_system.GlobalSecretType})
-
-	for i := range All {
-		if All[i].Singular == "health-check" {
-			// Preserving incoherency between kumactl and the web-service.
-			All[i].Singular = "healthcheck"
-			All[i].Plural = "healthchecks"
-		}
-		ByName[All[i].Singular] = All[i]
-		Names = append(Names, All[i].Singular)
-	}
-}
diff --git a/app/kumactl/pkg/entities/generated_mesh_entities.go b/app/kumactl/pkg/entities/generated_mesh_entities.go
deleted file mode 100644
index 2eebca546..000000000
--- a/app/kumactl/pkg/entities/generated_mesh_entities.go
+++ /dev/null
@@ -1,116 +0,0 @@
-// Generated by tools/resource-gen.
-// Run "make generate" to update this file.
-// nolint:whitespace
-package entities
-
-import (
-	"github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
-)
-
-var meshEntities = []Definition{
-
-	{
-		Singular:     "circuit-breaker",
-		Plural:       "circuit-breakers",
-		ResourceType: mesh.CircuitBreakerType,
-		ReadOnly:     false,
-	},
-
-	{
-		Singular:     "dataplane",
-		Plural:       "dataplanes",
-		ResourceType: mesh.DataplaneType,
-		ReadOnly:     false,
-	},
-
-	{
-		Singular:     "external-service",
-		Plural:       "external-services",
-		ResourceType: mesh.ExternalServiceType,
-		ReadOnly:     false,
-	},
-
-	{
-		Singular:     "fault-injection",
-		Plural:       "fault-injections",
-		ResourceType: mesh.FaultInjectionType,
-		ReadOnly:     false,
-	},
-
-	{
-		Singular:     "health-check",
-		Plural:       "health-checks",
-		ResourceType: mesh.HealthCheckType,
-		ReadOnly:     false,
-	},
-
-	{
-		Singular:     "mesh",
-		Plural:       "meshes",
-		ResourceType: mesh.MeshType,
-		ReadOnly:     false,
-	},
-
-	{
-		Singular:     "proxytemplate",
-		Plural:       "proxytemplates",
-		ResourceType: mesh.ProxyTemplateType,
-		ReadOnly:     false,
-	},
-
-	{
-		Singular:     "rate-limit",
-		Plural:       "rate-limits",
-		ResourceType: mesh.RateLimitType,
-		ReadOnly:     false,
-	},
-
-	{
-		Singular:     "retry",
-		Plural:       "retries",
-		ResourceType: mesh.RetryType,
-		ReadOnly:     false,
-	},
-
-	{
-		Singular:     "timeout",
-		Plural:       "timeouts",
-		ResourceType: mesh.TimeoutType,
-		ReadOnly:     false,
-	},
-
-	{
-		Singular:     "traffic-log",
-		Plural:       "traffic-logs",
-		ResourceType: mesh.TrafficLogType,
-		ReadOnly:     false,
-	},
-
-	{
-		Singular:     "traffic-permission",
-		Plural:       "traffic-permissions",
-		ResourceType: mesh.TrafficPermissionType,
-		ReadOnly:     false,
-	},
-
-	{
-		Singular:     "traffic-route",
-		Plural:       "traffic-routes",
-		ResourceType: mesh.TrafficRouteType,
-		ReadOnly:     false,
-	},
-
-	{
-		Singular:     "traffic-trace",
-		Plural:       "traffic-traces",
-		ResourceType: mesh.TrafficTraceType,
-		ReadOnly:     false,
-	},
-
-	{
-		Singular:     "zone-ingress",
-		Plural:       "zone-ingresses",
-		ResourceType: mesh.ZoneIngressType,
-		ReadOnly:     false,
-	},
-}
diff --git a/app/kumactl/pkg/entities/generated_system_entities.go b/app/kumactl/pkg/entities/generated_system_entities.go
deleted file mode 100644
index 3be80b6ba..000000000
--- a/app/kumactl/pkg/entities/generated_system_entities.go
+++ /dev/null
@@ -1,25 +0,0 @@
-// Generated by tools/resource-gen.
-// Run "make generate" to update this file.
-// nolint:whitespace
-package entities
-
-import (
-	"github.com/kumahq/kuma/pkg/core/resources/apis/system"
-)
-
-var systemEntities = []Definition{
-
-	{
-		Singular:     "secret",
-		Plural:       "secrets",
-		ResourceType: system.SecretType,
-		ReadOnly:     false,
-	},
-
-	{
-		Singular:     "zone",
-		Plural:       "zones",
-		ResourceType: system.ZoneType,
-		ReadOnly:     false,
-	},
-}
diff --git a/app/kumactl/pkg/resources/store.go b/app/kumactl/pkg/resources/store.go
index 6468383bd..0f1eb3115 100644
--- a/app/kumactl/pkg/resources/store.go
+++ b/app/kumactl/pkg/resources/store.go
@@ -2,16 +2,23 @@ package resources
 
 import (
 	kumactl_client "github.com/kumahq/kuma/app/kumactl/pkg/client"
-	kuma_rest "github.com/kumahq/kuma/pkg/api-server/definitions"
 	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
+	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
+	core_rest "github.com/kumahq/kuma/pkg/core/resources/model/rest"
 	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
 	remote_resources "github.com/kumahq/kuma/pkg/plugins/resources/remote"
 )
 
-func NewResourceStore(coordinates *config_proto.ControlPlaneCoordinates_ApiServer) (core_store.ResourceStore, error) {
+func NewResourceStore(coordinates *config_proto.ControlPlaneCoordinates_ApiServer, defs []core_model.ResourceTypeDescriptor) (core_store.ResourceStore, error) {
 	client, err := kumactl_client.ApiServerClient(coordinates)
 	if err != nil {
 		return nil, err
 	}
-	return remote_resources.NewStore(client, kuma_rest.AllApis()), nil
+	mapping := make(map[core_model.ResourceType]core_rest.ResourceApi)
+	for _, ws := range defs {
+		mapping[ws.Name] = core_rest.NewResourceApi(ws.Scope, ws.WsPath)
+	}
+	return remote_resources.NewStore(client, &core_rest.ApiDescriptor{
+		Resources: mapping,
+	}), nil
 }
diff --git a/mk/generate.mk b/mk/generate.mk
index 191497b2e..02a636e12 100644
--- a/mk/generate.mk
+++ b/mk/generate.mk
@@ -23,24 +23,12 @@ 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/type resources/ws resources/kds generate/deepcopy
-
-.PHONY: resources/kds
-resources/kds:
-	$(GO_RUN) ./tools/resource-gen.go -generator kds
+generate: clean/proto protoc/pkg/config/app/kumactl/v1alpha1 protoc/pkg/test/apis/sample/v1alpha1 protoc/plugins resources/type generate/deepcopy
 
 .PHONY: resources/type
 resources/type:
 	$(GO_RUN) ./tools/resource-gen.go -generator type
 
-.PHONY: resources/ws
-resources/ws:
-	$(GO_RUN) ./tools/resource-gen.go -generator ws
-
-.PHONY: resources/kumactl
-resources/kumactl:
-	$(GO_RUN) ./tools/resource-gen.go -generator kumactl
-
 .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/api-server/definitions/api.go b/pkg/api-server/definitions/api.go
deleted file mode 100644
index af8b8bb28..000000000
--- a/pkg/api-server/definitions/api.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package definitions
-
-import (
-	"github.com/kumahq/kuma/pkg/core/resources/apis/system"
-	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
-	core_rest "github.com/kumahq/kuma/pkg/core/resources/model/rest"
-	"github.com/kumahq/kuma/pkg/core/resources/registry"
-)
-
-type ResourceWsDefinition struct {
-	Type      core_model.ResourceType
-	Path      string
-	ReadOnly  bool
-	AdminOnly bool
-}
-
-func (ws *ResourceWsDefinition) ResourceFactory() core_model.Resource {
-	m, err := registry.Global().NewObject(ws.Type)
-	if err != nil {
-		panic(err.Error())
-	}
-
-	return m
-}
-
-func (ws *ResourceWsDefinition) ResourceListFactory() core_model.ResourceList {
-	l, err := registry.Global().NewList(ws.Type)
-	if err != nil {
-		panic(err.Error())
-	}
-
-	return l
-}
-
-var All = append(append([]ResourceWsDefinition{
-	{
-		Type:      system.GlobalSecretType,
-		Path:      "global-secrets",
-		AdminOnly: true,
-	},
-}, systemWsDefinitions...), meshWsDefinitions...)
-
-func AllApis() core_rest.Api {
-	mapping := make(map[core_model.ResourceType]core_rest.ResourceApi)
-	for _, ws := range All {
-		mapping[ws.Type] = core_rest.NewResourceApi(ws.Type, ws.Path)
-	}
-	return &core_rest.ApiDescriptor{
-		Resources: mapping,
-	}
-}
diff --git a/pkg/api-server/definitions/generated_mesh_definitions.go b/pkg/api-server/definitions/generated_mesh_definitions.go
deleted file mode 100644
index 061977396..000000000
--- a/pkg/api-server/definitions/generated_mesh_definitions.go
+++ /dev/null
@@ -1,145 +0,0 @@
-// Generated by tools/resource-gen.
-// Run "make generate" to update this file.
-
-// nolint:whitespace
-package definitions
-
-import (
-	"github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
-)
-
-var meshWsDefinitions = []ResourceWsDefinition{
-
-	{
-		Type:      mesh.CircuitBreakerType,
-		Path:      "circuit-breakers",
-		AdminOnly: false,
-		ReadOnly:  false,
-	},
-
-	{
-		Type:      mesh.DataplaneType,
-		Path:      "dataplanes",
-		AdminOnly: false,
-		ReadOnly:  false,
-	},
-
-	{
-		Type:      mesh.DataplaneInsightType,
-		Path:      "dataplane-insights",
-		AdminOnly: false,
-		ReadOnly:  true,
-	},
-
-	{
-		Type:      mesh.ExternalServiceType,
-		Path:      "external-services",
-		AdminOnly: false,
-		ReadOnly:  false,
-	},
-
-	{
-		Type:      mesh.FaultInjectionType,
-		Path:      "fault-injections",
-		AdminOnly: false,
-		ReadOnly:  false,
-	},
-
-	{
-		Type:      mesh.HealthCheckType,
-		Path:      "health-checks",
-		AdminOnly: false,
-		ReadOnly:  false,
-	},
-
-	{
-		Type:      mesh.MeshType,
-		Path:      "meshes",
-		AdminOnly: false,
-		ReadOnly:  false,
-	},
-
-	{
-		Type:      mesh.MeshInsightType,
-		Path:      "mesh-insights",
-		AdminOnly: false,
-		ReadOnly:  true,
-	},
-
-	{
-		Type:      mesh.ProxyTemplateType,
-		Path:      "proxytemplates",
-		AdminOnly: false,
-		ReadOnly:  false,
-	},
-
-	{
-		Type:      mesh.RateLimitType,
-		Path:      "rate-limits",
-		AdminOnly: false,
-		ReadOnly:  false,
-	},
-
-	{
-		Type:      mesh.RetryType,
-		Path:      "retries",
-		AdminOnly: false,
-		ReadOnly:  false,
-	},
-
-	{
-		Type:      mesh.ServiceInsightType,
-		Path:      "service-insights",
-		AdminOnly: false,
-		ReadOnly:  true,
-	},
-
-	{
-		Type:      mesh.TimeoutType,
-		Path:      "timeouts",
-		AdminOnly: false,
-		ReadOnly:  false,
-	},
-
-	{
-		Type:      mesh.TrafficLogType,
-		Path:      "traffic-logs",
-		AdminOnly: false,
-		ReadOnly:  false,
-	},
-
-	{
-		Type:      mesh.TrafficPermissionType,
-		Path:      "traffic-permissions",
-		AdminOnly: false,
-		ReadOnly:  false,
-	},
-
-	{
-		Type:      mesh.TrafficRouteType,
-		Path:      "traffic-routes",
-		AdminOnly: false,
-		ReadOnly:  false,
-	},
-
-	{
-		Type:      mesh.TrafficTraceType,
-		Path:      "traffic-traces",
-		AdminOnly: false,
-		ReadOnly:  false,
-	},
-
-	{
-		Type:      mesh.ZoneIngressType,
-		Path:      "zone-ingresses",
-		AdminOnly: false,
-		ReadOnly:  false,
-	},
-
-	{
-		Type:      mesh.ZoneIngressInsightType,
-		Path:      "zone-ingress-insights",
-		AdminOnly: false,
-		ReadOnly:  true,
-	},
-}
diff --git a/pkg/api-server/definitions/generated_system_definitions.go b/pkg/api-server/definitions/generated_system_definitions.go
deleted file mode 100644
index 06d8c6641..000000000
--- a/pkg/api-server/definitions/generated_system_definitions.go
+++ /dev/null
@@ -1,33 +0,0 @@
-// Generated by tools/resource-gen.
-// Run "make generate" to update this file.
-
-// nolint:whitespace
-package definitions
-
-import (
-	"github.com/kumahq/kuma/pkg/core/resources/apis/system"
-)
-
-var systemWsDefinitions = []ResourceWsDefinition{
-
-	{
-		Type:      system.SecretType,
-		Path:      "secrets",
-		AdminOnly: true,
-		ReadOnly:  false,
-	},
-
-	{
-		Type:      system.ZoneType,
-		Path:      "zones",
-		AdminOnly: false,
-		ReadOnly:  false,
-	},
-
-	{
-		Type:      system.ZoneInsightType,
-		Path:      "zone-insights",
-		AdminOnly: false,
-		ReadOnly:  true,
-	},
-}
diff --git a/pkg/api-server/resource_endpoints.go b/pkg/api-server/resource_endpoints.go
index f026e22d0..acd67059b 100644
--- a/pkg/api-server/resource_endpoints.go
+++ b/pkg/api-server/resource_endpoints.go
@@ -8,7 +8,6 @@ import (
 	"github.com/emicklei/go-restful"
 
 	"github.com/kumahq/kuma/pkg/api-server/authz"
-	"github.com/kumahq/kuma/pkg/api-server/definitions"
 	config_core "github.com/kumahq/kuma/pkg/config/core"
 	"github.com/kumahq/kuma/pkg/core"
 	"github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
@@ -33,12 +32,12 @@ const (
 type resourceEndpoints struct {
 	mode       config_core.CpMode
 	resManager manager.ResourceManager
-	definitions.ResourceWsDefinition
-	adminAuth authz.AdminAuth
+	descriptor model.ResourceTypeDescriptor
+	adminAuth  authz.AdminAuth
 }
 
 func (r *resourceEndpoints) auth() restful.FilterFunction {
-	if r.ResourceWsDefinition.AdminOnly {
+	if r.descriptor.AdminOnly {
 		return r.adminAuth.Validate
 	}
 	return authz.NoAuth
@@ -47,8 +46,8 @@ func (r *resourceEndpoints) auth() restful.FilterFunction {
 func (r *resourceEndpoints) addFindEndpoint(ws *restful.WebService, pathPrefix string) {
 	ws.Route(ws.GET(pathPrefix+"/{name}").To(r.findResource).
 		Filter(r.auth()).
-		Doc(fmt.Sprintf("Get a %s", r.Path)).
-		Param(ws.PathParameter("name", fmt.Sprintf("Name of a %s", r.Path)).DataType("string")).
+		Doc(fmt.Sprintf("Get a %s", r.descriptor.WsPath)).
+		Param(ws.PathParameter("name", fmt.Sprintf("Name of a %s", r.descriptor.Name)).DataType("string")).
 		Returns(200, "OK", nil).
 		Returns(404, "Not found", nil))
 }
@@ -57,7 +56,7 @@ func (r *resourceEndpoints) findResource(request *restful.Request, response *res
 	name := request.PathParameter("name")
 	meshName := r.meshFromRequest(request)
 
-	resource := r.ResourceFactory()
+	resource := r.descriptor.NewObject()
 	err := r.resManager.Get(request.Request.Context(), resource, store.GetByKey(name, meshName))
 	if err != nil {
 		rest_errors.HandleError(response, err, "Could not retrieve a resource")
@@ -72,7 +71,7 @@ func (r *resourceEndpoints) findResource(request *restful.Request, response *res
 func (r *resourceEndpoints) addListEndpoint(ws *restful.WebService, pathPrefix string) {
 	ws.Route(ws.GET(pathPrefix).To(r.listResources).
 		Filter(r.auth()).
-		Doc(fmt.Sprintf("List of %s", r.Path)).
+		Doc(fmt.Sprintf("List of %s", r.descriptor.Name)).
 		Param(ws.PathParameter("size", "size of page").DataType("int")).
 		Param(ws.PathParameter("offset", "offset of page to list").DataType("string")).
 		Returns(200, "OK", nil))
@@ -87,7 +86,7 @@ func (r *resourceEndpoints) listResources(request *restful.Request, response *re
 		return
 	}
 
-	list := r.ResourceListFactory()
+	list := r.descriptor.NewList()
 	if err := r.resManager.List(request.Request.Context(), list, store.ListByMesh(meshName), store.ListByPage(page.size, page.offset)); err != nil {
 		rest_errors.HandleError(response, err, "Could not retrieve resources")
 	} else {
@@ -100,15 +99,15 @@ func (r *resourceEndpoints) listResources(request *restful.Request, response *re
 }
 
 func (r *resourceEndpoints) addCreateOrUpdateEndpoint(ws *restful.WebService, pathPrefix string) {
-	if r.ReadOnly {
+	if r.descriptor.ReadOnly {
 		ws.Route(ws.PUT(pathPrefix+"/{name}").To(r.createOrUpdateResourceReadOnly).
 			Doc("Not allowed in read-only mode.").
 			Returns(http.StatusMethodNotAllowed, "Not allowed in read-only mode.", restful.ServiceError{}))
 	} else {
 		ws.Route(ws.PUT(pathPrefix+"/{name}").To(r.createOrUpdateResource).
 			Filter(r.auth()).
-			Doc(fmt.Sprintf("Updates a %s", r.Path)).
-			Param(ws.PathParameter("name", fmt.Sprintf("Name of the %s", r.Path)).DataType("string")).
+			Doc(fmt.Sprintf("Updates a %s", r.descriptor.WsPath)).
+			Param(ws.PathParameter("name", fmt.Sprintf("Name of the %s", r.descriptor.WsPath)).DataType("string")).
 			Returns(200, "OK", nil).
 			Returns(201, "Created", nil))
 	}
@@ -119,7 +118,7 @@ func (r *resourceEndpoints) createOrUpdateResource(request *restful.Request, res
 	meshName := r.meshFromRequest(request)
 
 	resourceRes := rest.Resource{
-		Spec: r.ResourceFactory().GetSpec(),
+		Spec: r.descriptor.NewObject().GetSpec(),
 	}
 
 	if err := request.ReadEntity(&resourceRes); err != nil {
@@ -132,7 +131,7 @@ func (r *resourceEndpoints) createOrUpdateResource(request *restful.Request, res
 		return
 	}
 
-	resource := r.ResourceFactory()
+	resource := r.descriptor.NewObject()
 	if err := r.resManager.Get(request.Request.Context(), resource, store.GetByKey(name, meshName)); err != nil {
 		if store.IsResourceNotFound(err) {
 			r.createResource(request.Request.Context(), name, meshName, resourceRes.Spec, response)
@@ -145,7 +144,7 @@ func (r *resourceEndpoints) createOrUpdateResource(request *restful.Request, res
 }
 
 func (r *resourceEndpoints) createResource(ctx context.Context, name string, meshName string, spec model.ResourceSpec, response *restful.Response) {
-	res := r.ResourceFactory()
+	res := r.descriptor.NewObject()
 	_ = res.SetSpec(spec)
 	if err := r.resManager.Create(ctx, res, store.CreateByKey(name, meshName)); err != nil {
 		rest_errors.HandleError(response, err, "Could not create a resource")
@@ -171,15 +170,15 @@ func (r *resourceEndpoints) createOrUpdateResourceReadOnly(request *restful.Requ
 }
 
 func (r *resourceEndpoints) addDeleteEndpoint(ws *restful.WebService, pathPrefix string) {
-	if r.ReadOnly {
+	if r.descriptor.ReadOnly {
 		ws.Route(ws.DELETE(pathPrefix+"/{name}").To(r.deleteResourceReadOnly).
 			Doc("Not allowed in read-only mode.").
 			Returns(http.StatusMethodNotAllowed, "Not allowed in read-only mode.", restful.ServiceError{}))
 	} else {
 		ws.Route(ws.DELETE(pathPrefix+"/{name}").To(r.deleteResource).
 			Filter(r.auth()).
-			Doc(fmt.Sprintf("Deletes a %s", r.Path)).
-			Param(ws.PathParameter("name", fmt.Sprintf("Name of a %s", r.Path)).DataType("string")).
+			Doc(fmt.Sprintf("Deletes a %s", r.descriptor.Name)).
+			Param(ws.PathParameter("name", fmt.Sprintf("Name of a %s", r.descriptor.Name)).DataType("string")).
 			Returns(200, "OK", nil))
 	}
 }
@@ -188,7 +187,7 @@ func (r *resourceEndpoints) deleteResource(request *restful.Request, response *r
 	name := request.PathParameter("name")
 	meshName := r.meshFromRequest(request)
 
-	resource := r.ResourceFactory()
+	resource := r.descriptor.NewObject()
 	if err := r.resManager.Delete(request.Request.Context(), resource, store.DeleteByKey(name, meshName)); err != nil {
 		rest_errors.HandleError(response, err, "Could not delete a resource")
 	}
@@ -209,18 +208,18 @@ func (r *resourceEndpoints) validateResourceRequest(request *restful.Request, re
 	if name != resource.Meta.Name {
 		err.AddViolation("name", "name from the URL has to be the same as in body")
 	}
-	if string(r.ResourceFactory().GetType()) != resource.Meta.Type {
+	if string(r.descriptor.Name) != resource.Meta.Type {
 		err.AddViolation("type", "type from the URL has to be the same as in body")
 	}
-	if r.ResourceFactory().Scope() == model.ScopeMesh && meshName != resource.Meta.Mesh {
+	if r.descriptor.Scope == model.ScopeMesh && meshName != resource.Meta.Mesh {
 		err.AddViolation("mesh", "mesh from the URL has to be the same as in body")
 	}
-	err.AddError("", mesh.ValidateMeta(name, meshName, r.ResourceFactory().Scope()))
+	err.AddError("", mesh.ValidateMeta(name, meshName, r.descriptor.Scope))
 	return err.OrNil()
 }
 
 func (r *resourceEndpoints) meshFromRequest(request *restful.Request) string {
-	if r.ResourceFactory().Scope() == model.ScopeMesh {
+	if r.descriptor.Scope == model.ScopeMesh {
 		return request.PathParameter("mesh")
 	}
 	return ""
diff --git a/pkg/api-server/server.go b/pkg/api-server/server.go
index 8145202d5..5d1d96053 100644
--- a/pkg/api-server/server.go
+++ b/pkg/api-server/server.go
@@ -22,7 +22,6 @@ import (
 	"github.com/kumahq/kuma/app/kuma-ui/pkg/resources"
 	"github.com/kumahq/kuma/pkg/api-server/authz"
 	"github.com/kumahq/kuma/pkg/api-server/customization"
-	"github.com/kumahq/kuma/pkg/api-server/definitions"
 	api_server "github.com/kumahq/kuma/pkg/config/api-server"
 	kuma_cp "github.com/kumahq/kuma/pkg/config/app/kuma-cp"
 	config_core "github.com/kumahq/kuma/pkg/config/core"
@@ -30,6 +29,7 @@ import (
 	"github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	"github.com/kumahq/kuma/pkg/core/resources/manager"
 	"github.com/kumahq/kuma/pkg/core/resources/model"
+	"github.com/kumahq/kuma/pkg/core/resources/registry"
 	"github.com/kumahq/kuma/pkg/core/runtime"
 	"github.com/kumahq/kuma/pkg/metrics"
 	"github.com/kumahq/kuma/pkg/tokens/builtin"
@@ -72,7 +72,7 @@ func init() {
 	}
 }
 
-func NewApiServer(resManager manager.ResourceManager, wsManager customization.APIInstaller, defs []definitions.ResourceWsDefinition, cfg *kuma_cp.Config, enableGUI bool, metrics metrics.Metrics) (*ApiServer, error) {
+func NewApiServer(resManager manager.ResourceManager, wsManager customization.APIInstaller, defs []model.ResourceTypeDescriptor, cfg *kuma_cp.Config, enableGUI bool, metrics metrics.Metrics) (*ApiServer, error) {
 	serverConfig := cfg.ApiServer
 	container := restful.NewContainer()
 
@@ -143,7 +143,7 @@ func NewApiServer(resManager manager.ResourceManager, wsManager customization.AP
 	return newApiServer, nil
 }
 
-func addResourcesEndpoints(ws *restful.WebService, defs []definitions.ResourceWsDefinition, resManager manager.ResourceManager, cfg *kuma_cp.Config) {
+func addResourcesEndpoints(ws *restful.WebService, defs []model.ResourceTypeDescriptor, resManager manager.ResourceManager, cfg *kuma_cp.Config) {
 	dpOverviewEndpoints := dataplaneOverviewEndpoints{
 		resManager: resManager,
 	}
@@ -164,14 +164,14 @@ func addResourcesEndpoints(ws *restful.WebService, defs []definitions.ResourceWs
 	zoneIngressOverviewEndpoints.addListEndpoint(ws)
 
 	for _, definition := range defs {
-		defType := definition.Type
+		defType := definition.Name
 		if cfg.ApiServer.ReadOnly || (defType == mesh.DataplaneType && cfg.Mode == config_core.Global) || (defType != mesh.DataplaneType && cfg.Mode == config_core.Zone) {
 			definition.ReadOnly = true
 		}
 		endpoints := resourceEndpoints{
-			mode:                 cfg.Mode,
-			resManager:           resManager,
-			ResourceWsDefinition: definition,
+			mode:       cfg.Mode,
+			resManager: resManager,
+			descriptor: definition,
 			adminAuth: authz.AdminAuth{
 				AllowFromLocalhost: cfg.ApiServer.Auth.AllowFromLocalhost,
 			},
@@ -180,24 +180,24 @@ func addResourcesEndpoints(ws *restful.WebService, defs []definitions.ResourceWs
 		case mesh.ServiceInsightType:
 			// ServiceInsight is a bit different
 			ep := serviceInsightEndpoints{endpoints}
-			ep.addCreateOrUpdateEndpoint(ws, "/meshes/{mesh}/"+definition.Path)
-			ep.addDeleteEndpoint(ws, "/meshes/{mesh}/"+definition.Path)
-			ep.addFindEndpoint(ws, "/meshes/{mesh}/"+definition.Path)
-			ep.addListEndpoint(ws, "/meshes/{mesh}/"+definition.Path)
-			ep.addListEndpoint(ws, "/"+definition.Path) // listing all resources in all meshes
+			ep.addCreateOrUpdateEndpoint(ws, "/meshes/{mesh}/"+definition.WsPath)
+			ep.addDeleteEndpoint(ws, "/meshes/{mesh}/"+definition.WsPath)
+			ep.addFindEndpoint(ws, "/meshes/{mesh}/"+definition.WsPath)
+			ep.addListEndpoint(ws, "/meshes/{mesh}/"+definition.WsPath)
+			ep.addListEndpoint(ws, "/"+definition.WsPath) // listing all resources in all meshes
 		default:
-			switch definition.ResourceFactory().Scope() {
+			switch definition.Scope {
 			case model.ScopeMesh:
-				endpoints.addCreateOrUpdateEndpoint(ws, "/meshes/{mesh}/"+definition.Path)
-				endpoints.addDeleteEndpoint(ws, "/meshes/{mesh}/"+definition.Path)
-				endpoints.addFindEndpoint(ws, "/meshes/{mesh}/"+definition.Path)
-				endpoints.addListEndpoint(ws, "/meshes/{mesh}/"+definition.Path)
-				endpoints.addListEndpoint(ws, "/"+definition.Path) // listing all resources in all meshes
+				endpoints.addCreateOrUpdateEndpoint(ws, "/meshes/{mesh}/"+definition.WsPath)
+				endpoints.addDeleteEndpoint(ws, "/meshes/{mesh}/"+definition.WsPath)
+				endpoints.addFindEndpoint(ws, "/meshes/{mesh}/"+definition.WsPath)
+				endpoints.addListEndpoint(ws, "/meshes/{mesh}/"+definition.WsPath)
+				endpoints.addListEndpoint(ws, "/"+definition.WsPath) // listing all resources in all meshes
 			case model.ScopeGlobal:
-				endpoints.addCreateOrUpdateEndpoint(ws, "/"+definition.Path)
-				endpoints.addDeleteEndpoint(ws, "/"+definition.Path)
-				endpoints.addFindEndpoint(ws, "/"+definition.Path)
-				endpoints.addListEndpoint(ws, "/"+definition.Path)
+				endpoints.addCreateOrUpdateEndpoint(ws, "/"+definition.WsPath)
+				endpoints.addDeleteEndpoint(ws, "/"+definition.WsPath)
+				endpoints.addFindEndpoint(ws, "/"+definition.WsPath)
+				endpoints.addListEndpoint(ws, "/"+definition.WsPath)
 			}
 		}
 	}
@@ -338,7 +338,7 @@ func (a *ApiServer) notAvailableHandler(writer http.ResponseWriter, request *htt
 
 func SetupServer(rt runtime.Runtime) error {
 	cfg := rt.Config()
-	apiServer, err := NewApiServer(rt.ResourceManager(), rt.APIInstaller(), definitions.All, &cfg, cfg.Mode != config_core.Zone, rt.Metrics())
+	apiServer, err := NewApiServer(rt.ResourceManager(), rt.APIInstaller(), registry.Global().ObjectDescriptors(model.HasWsEnabled()), &cfg, cfg.Mode != config_core.Zone, rt.Metrics())
 	if err != nil {
 		return err
 	}
diff --git a/pkg/api-server/service_insight_endpoints.go b/pkg/api-server/service_insight_endpoints.go
index 2d789d5d0..e4b46edfa 100644
--- a/pkg/api-server/service_insight_endpoints.go
+++ b/pkg/api-server/service_insight_endpoints.go
@@ -23,8 +23,8 @@ type serviceInsightEndpoints struct {
 func (s *serviceInsightEndpoints) addFindEndpoint(ws *restful.WebService, pathPrefix string) {
 	ws.Route(ws.GET(pathPrefix+"/{service}").To(s.findResource).
 		Filter(s.auth()).
-		Doc(fmt.Sprintf("Get a %s", s.Path)).
-		Param(ws.PathParameter("service", fmt.Sprintf("Name of a %s", s.Path)).DataType("string")).
+		Doc(fmt.Sprintf("Get a %s", s.descriptor.WsPath)).
+		Param(ws.PathParameter("service", fmt.Sprintf("Name of a %s", s.descriptor.Name)).DataType("string")).
 		Returns(200, "OK", nil).
 		Returns(404, "Not found", nil))
 }
@@ -56,7 +56,7 @@ func (s *serviceInsightEndpoints) findResource(request *restful.Request, respons
 func (s *serviceInsightEndpoints) addListEndpoint(ws *restful.WebService, pathPrefix string) {
 	ws.Route(ws.GET(pathPrefix).To(s.listResources).
 		Filter(s.auth()).
-		Doc(fmt.Sprintf("List of %s", s.Path)).
+		Doc(fmt.Sprintf("List of %s", s.descriptor.Name)).
 		Param(ws.PathParameter("size", "size of page").DataType("int")).
 		Param(ws.PathParameter("offset", "offset of page to list").DataType("string")).
 		Returns(200, "OK", nil))
diff --git a/pkg/core/resources/apis/mesh/generated_resources.go b/pkg/core/resources/apis/mesh/generated_resources.go
index b84ba5457..d8d4d8eef 100644
--- a/pkg/core/resources/apis/mesh/generated_resources.go
+++ b/pkg/core/resources/apis/mesh/generated_resources.go
@@ -29,10 +29,6 @@ func NewCircuitBreakerResource() *CircuitBreakerResource {
 	}
 }
 
-func (t *CircuitBreakerResource) GetType() model.ResourceType {
-	return CircuitBreakerType
-}
-
 func (t *CircuitBreakerResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -63,10 +59,8 @@ func (t *CircuitBreakerResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *CircuitBreakerResource) Scope() model.ResourceScope {
-
-	return model.ScopeMesh
-
+func (t *CircuitBreakerResource) Descriptor() model.ResourceTypeDescriptor {
+	return CircuitBreakerResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &CircuitBreakerResourceList{}
@@ -105,9 +99,21 @@ func (l *CircuitBreakerResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var CircuitBreakerResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           CircuitBreakerType,
+	Resource:       NewCircuitBreakerResource(),
+	ResourceList:   &CircuitBreakerResourceList{},
+	ReadOnly:       false,
+	AdminOnly:      false,
+	Scope:          model.ScopeMesh,
+	KDSFlags:       model.FromGlobalToZone,
+	WsPath:         "circuit-breakers",
+	KumactlArg:     "circuit-breaker",
+	KumactlListArg: "circuit-breakers",
+}
+
 func init() {
-	registry.RegisterType(NewCircuitBreakerResource())
-	registry.RegistryListType(&CircuitBreakerResourceList{})
+	registry.RegisterType(CircuitBreakerResourceTypeDescriptor)
 }
 
 const (
@@ -127,10 +133,6 @@ func NewDataplaneResource() *DataplaneResource {
 	}
 }
 
-func (t *DataplaneResource) GetType() model.ResourceType {
-	return DataplaneType
-}
-
 func (t *DataplaneResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -153,10 +155,8 @@ func (t *DataplaneResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *DataplaneResource) Scope() model.ResourceScope {
-
-	return model.ScopeMesh
-
+func (t *DataplaneResource) Descriptor() model.ResourceTypeDescriptor {
+	return DataplaneResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &DataplaneResourceList{}
@@ -195,9 +195,21 @@ func (l *DataplaneResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var DataplaneResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           DataplaneType,
+	Resource:       NewDataplaneResource(),
+	ResourceList:   &DataplaneResourceList{},
+	ReadOnly:       false,
+	AdminOnly:      false,
+	Scope:          model.ScopeMesh,
+	KDSFlags:       model.FromZoneToGlobal | model.FromGlobalToZone,
+	WsPath:         "dataplanes",
+	KumactlArg:     "dataplane",
+	KumactlListArg: "dataplanes",
+}
+
 func init() {
-	registry.RegisterType(NewDataplaneResource())
-	registry.RegistryListType(&DataplaneResourceList{})
+	registry.RegisterType(DataplaneResourceTypeDescriptor)
 }
 
 const (
@@ -217,10 +229,6 @@ func NewDataplaneInsightResource() *DataplaneInsightResource {
 	}
 }
 
-func (t *DataplaneInsightResource) GetType() model.ResourceType {
-	return DataplaneInsightType
-}
-
 func (t *DataplaneInsightResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -247,10 +255,8 @@ func (t *DataplaneInsightResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *DataplaneInsightResource) Scope() model.ResourceScope {
-
-	return model.ScopeMesh
-
+func (t *DataplaneInsightResource) Descriptor() model.ResourceTypeDescriptor {
+	return DataplaneInsightResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &DataplaneInsightResourceList{}
@@ -289,9 +295,21 @@ func (l *DataplaneInsightResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var DataplaneInsightResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           DataplaneInsightType,
+	Resource:       NewDataplaneInsightResource(),
+	ResourceList:   &DataplaneInsightResourceList{},
+	ReadOnly:       true,
+	AdminOnly:      false,
+	Scope:          model.ScopeMesh,
+	KDSFlags:       model.FromZoneToGlobal,
+	WsPath:         "dataplane-insights",
+	KumactlArg:     "",
+	KumactlListArg: "",
+}
+
 func init() {
-	registry.RegisterType(NewDataplaneInsightResource())
-	registry.RegistryListType(&DataplaneInsightResourceList{})
+	registry.RegisterType(DataplaneInsightResourceTypeDescriptor)
 }
 
 const (
@@ -311,10 +329,6 @@ func NewDataplaneOverviewResource() *DataplaneOverviewResource {
 	}
 }
 
-func (t *DataplaneOverviewResource) GetType() model.ResourceType {
-	return DataplaneOverviewType
-}
-
 func (t *DataplaneOverviewResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -341,10 +355,8 @@ func (t *DataplaneOverviewResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *DataplaneOverviewResource) Scope() model.ResourceScope {
-
-	return model.ScopeMesh
-
+func (t *DataplaneOverviewResource) Descriptor() model.ResourceTypeDescriptor {
+	return DataplaneOverviewResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &DataplaneOverviewResourceList{}
@@ -383,6 +395,18 @@ func (l *DataplaneOverviewResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var DataplaneOverviewResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           DataplaneOverviewType,
+	Resource:       NewDataplaneOverviewResource(),
+	ResourceList:   &DataplaneOverviewResourceList{},
+	ReadOnly:       false,
+	AdminOnly:      false,
+	Scope:          model.ScopeMesh,
+	WsPath:         "",
+	KumactlArg:     "",
+	KumactlListArg: "",
+}
+
 const (
 	ExternalServiceType model.ResourceType = "ExternalService"
 )
@@ -400,10 +424,6 @@ func NewExternalServiceResource() *ExternalServiceResource {
 	}
 }
 
-func (t *ExternalServiceResource) GetType() model.ResourceType {
-	return ExternalServiceType
-}
-
 func (t *ExternalServiceResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -426,10 +446,8 @@ func (t *ExternalServiceResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *ExternalServiceResource) Scope() model.ResourceScope {
-
-	return model.ScopeMesh
-
+func (t *ExternalServiceResource) Descriptor() model.ResourceTypeDescriptor {
+	return ExternalServiceResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &ExternalServiceResourceList{}
@@ -468,9 +486,21 @@ func (l *ExternalServiceResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var ExternalServiceResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           ExternalServiceType,
+	Resource:       NewExternalServiceResource(),
+	ResourceList:   &ExternalServiceResourceList{},
+	ReadOnly:       false,
+	AdminOnly:      false,
+	Scope:          model.ScopeMesh,
+	KDSFlags:       model.FromGlobalToZone,
+	WsPath:         "external-services",
+	KumactlArg:     "external-service",
+	KumactlListArg: "external-services",
+}
+
 func init() {
-	registry.RegisterType(NewExternalServiceResource())
-	registry.RegistryListType(&ExternalServiceResourceList{})
+	registry.RegisterType(ExternalServiceResourceTypeDescriptor)
 }
 
 const (
@@ -490,10 +520,6 @@ func NewFaultInjectionResource() *FaultInjectionResource {
 	}
 }
 
-func (t *FaultInjectionResource) GetType() model.ResourceType {
-	return FaultInjectionType
-}
-
 func (t *FaultInjectionResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -524,10 +550,8 @@ func (t *FaultInjectionResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *FaultInjectionResource) Scope() model.ResourceScope {
-
-	return model.ScopeMesh
-
+func (t *FaultInjectionResource) Descriptor() model.ResourceTypeDescriptor {
+	return FaultInjectionResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &FaultInjectionResourceList{}
@@ -566,9 +590,21 @@ func (l *FaultInjectionResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var FaultInjectionResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           FaultInjectionType,
+	Resource:       NewFaultInjectionResource(),
+	ResourceList:   &FaultInjectionResourceList{},
+	ReadOnly:       false,
+	AdminOnly:      false,
+	Scope:          model.ScopeMesh,
+	KDSFlags:       model.FromGlobalToZone,
+	WsPath:         "fault-injections",
+	KumactlArg:     "fault-injection",
+	KumactlListArg: "fault-injections",
+}
+
 func init() {
-	registry.RegisterType(NewFaultInjectionResource())
-	registry.RegistryListType(&FaultInjectionResourceList{})
+	registry.RegisterType(FaultInjectionResourceTypeDescriptor)
 }
 
 const (
@@ -588,10 +624,6 @@ func NewGatewayResource() *GatewayResource {
 	}
 }
 
-func (t *GatewayResource) GetType() model.ResourceType {
-	return GatewayType
-}
-
 func (t *GatewayResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -618,10 +650,8 @@ func (t *GatewayResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *GatewayResource) Scope() model.ResourceScope {
-
-	return model.ScopeMesh
-
+func (t *GatewayResource) Descriptor() model.ResourceTypeDescriptor {
+	return GatewayResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &GatewayResourceList{}
@@ -660,6 +690,19 @@ func (l *GatewayResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var GatewayResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           GatewayType,
+	Resource:       NewGatewayResource(),
+	ResourceList:   &GatewayResourceList{},
+	ReadOnly:       false,
+	AdminOnly:      false,
+	Scope:          model.ScopeMesh,
+	KDSFlags:       model.FromZoneToGlobal,
+	WsPath:         "gateways",
+	KumactlArg:     "gateway",
+	KumactlListArg: "gateways",
+}
+
 const (
 	HealthCheckType model.ResourceType = "HealthCheck"
 )
@@ -677,10 +720,6 @@ func NewHealthCheckResource() *HealthCheckResource {
 	}
 }
 
-func (t *HealthCheckResource) GetType() model.ResourceType {
-	return HealthCheckType
-}
-
 func (t *HealthCheckResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -711,10 +750,8 @@ func (t *HealthCheckResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *HealthCheckResource) Scope() model.ResourceScope {
-
-	return model.ScopeMesh
-
+func (t *HealthCheckResource) Descriptor() model.ResourceTypeDescriptor {
+	return HealthCheckResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &HealthCheckResourceList{}
@@ -753,9 +790,21 @@ func (l *HealthCheckResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var HealthCheckResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           HealthCheckType,
+	Resource:       NewHealthCheckResource(),
+	ResourceList:   &HealthCheckResourceList{},
+	ReadOnly:       false,
+	AdminOnly:      false,
+	Scope:          model.ScopeMesh,
+	KDSFlags:       model.FromGlobalToZone,
+	WsPath:         "health-checks",
+	KumactlArg:     "healthcheck",
+	KumactlListArg: "healthchecks",
+}
+
 func init() {
-	registry.RegisterType(NewHealthCheckResource())
-	registry.RegistryListType(&HealthCheckResourceList{})
+	registry.RegisterType(HealthCheckResourceTypeDescriptor)
 }
 
 const (
@@ -775,10 +824,6 @@ func NewMeshResource() *MeshResource {
 	}
 }
 
-func (t *MeshResource) GetType() model.ResourceType {
-	return MeshType
-}
-
 func (t *MeshResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -801,10 +846,8 @@ func (t *MeshResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *MeshResource) Scope() model.ResourceScope {
-
-	return model.ScopeGlobal
-
+func (t *MeshResource) Descriptor() model.ResourceTypeDescriptor {
+	return MeshResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &MeshResourceList{}
@@ -843,9 +886,21 @@ func (l *MeshResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var MeshResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           MeshType,
+	Resource:       NewMeshResource(),
+	ResourceList:   &MeshResourceList{},
+	ReadOnly:       false,
+	AdminOnly:      false,
+	Scope:          model.ScopeGlobal,
+	KDSFlags:       model.FromGlobalToZone,
+	WsPath:         "meshes",
+	KumactlArg:     "mesh",
+	KumactlListArg: "meshes",
+}
+
 func init() {
-	registry.RegisterType(NewMeshResource())
-	registry.RegistryListType(&MeshResourceList{})
+	registry.RegisterType(MeshResourceTypeDescriptor)
 }
 
 const (
@@ -865,10 +920,6 @@ func NewMeshInsightResource() *MeshInsightResource {
 	}
 }
 
-func (t *MeshInsightResource) GetType() model.ResourceType {
-	return MeshInsightType
-}
-
 func (t *MeshInsightResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -895,10 +946,8 @@ func (t *MeshInsightResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *MeshInsightResource) Scope() model.ResourceScope {
-
-	return model.ScopeGlobal
-
+func (t *MeshInsightResource) Descriptor() model.ResourceTypeDescriptor {
+	return MeshInsightResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &MeshInsightResourceList{}
@@ -937,9 +986,20 @@ func (l *MeshInsightResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var MeshInsightResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           MeshInsightType,
+	Resource:       NewMeshInsightResource(),
+	ResourceList:   &MeshInsightResourceList{},
+	ReadOnly:       true,
+	AdminOnly:      false,
+	Scope:          model.ScopeGlobal,
+	WsPath:         "mesh-insights",
+	KumactlArg:     "",
+	KumactlListArg: "",
+}
+
 func init() {
-	registry.RegisterType(NewMeshInsightResource())
-	registry.RegistryListType(&MeshInsightResourceList{})
+	registry.RegisterType(MeshInsightResourceTypeDescriptor)
 }
 
 const (
@@ -959,10 +1019,6 @@ func NewProxyTemplateResource() *ProxyTemplateResource {
 	}
 }
 
-func (t *ProxyTemplateResource) GetType() model.ResourceType {
-	return ProxyTemplateType
-}
-
 func (t *ProxyTemplateResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -989,10 +1045,8 @@ func (t *ProxyTemplateResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *ProxyTemplateResource) Scope() model.ResourceScope {
-
-	return model.ScopeMesh
-
+func (t *ProxyTemplateResource) Descriptor() model.ResourceTypeDescriptor {
+	return ProxyTemplateResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &ProxyTemplateResourceList{}
@@ -1031,9 +1085,21 @@ func (l *ProxyTemplateResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var ProxyTemplateResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           ProxyTemplateType,
+	Resource:       NewProxyTemplateResource(),
+	ResourceList:   &ProxyTemplateResourceList{},
+	ReadOnly:       false,
+	AdminOnly:      false,
+	Scope:          model.ScopeMesh,
+	KDSFlags:       model.FromGlobalToZone,
+	WsPath:         "proxytemplates",
+	KumactlArg:     "proxytemplate",
+	KumactlListArg: "proxytemplates",
+}
+
 func init() {
-	registry.RegisterType(NewProxyTemplateResource())
-	registry.RegistryListType(&ProxyTemplateResourceList{})
+	registry.RegisterType(ProxyTemplateResourceTypeDescriptor)
 }
 
 const (
@@ -1053,10 +1119,6 @@ func NewRateLimitResource() *RateLimitResource {
 	}
 }
 
-func (t *RateLimitResource) GetType() model.ResourceType {
-	return RateLimitType
-}
-
 func (t *RateLimitResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -1087,10 +1149,8 @@ func (t *RateLimitResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *RateLimitResource) Scope() model.ResourceScope {
-
-	return model.ScopeMesh
-
+func (t *RateLimitResource) Descriptor() model.ResourceTypeDescriptor {
+	return RateLimitResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &RateLimitResourceList{}
@@ -1129,9 +1189,21 @@ func (l *RateLimitResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var RateLimitResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           RateLimitType,
+	Resource:       NewRateLimitResource(),
+	ResourceList:   &RateLimitResourceList{},
+	ReadOnly:       false,
+	AdminOnly:      false,
+	Scope:          model.ScopeMesh,
+	KDSFlags:       model.FromGlobalToZone,
+	WsPath:         "rate-limits",
+	KumactlArg:     "rate-limit",
+	KumactlListArg: "rate-limits",
+}
+
 func init() {
-	registry.RegisterType(NewRateLimitResource())
-	registry.RegistryListType(&RateLimitResourceList{})
+	registry.RegisterType(RateLimitResourceTypeDescriptor)
 }
 
 const (
@@ -1151,10 +1223,6 @@ func NewRetryResource() *RetryResource {
 	}
 }
 
-func (t *RetryResource) GetType() model.ResourceType {
-	return RetryType
-}
-
 func (t *RetryResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -1185,10 +1253,8 @@ func (t *RetryResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *RetryResource) Scope() model.ResourceScope {
-
-	return model.ScopeMesh
-
+func (t *RetryResource) Descriptor() model.ResourceTypeDescriptor {
+	return RetryResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &RetryResourceList{}
@@ -1227,9 +1293,21 @@ func (l *RetryResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var RetryResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           RetryType,
+	Resource:       NewRetryResource(),
+	ResourceList:   &RetryResourceList{},
+	ReadOnly:       false,
+	AdminOnly:      false,
+	Scope:          model.ScopeMesh,
+	KDSFlags:       model.FromGlobalToZone,
+	WsPath:         "retries",
+	KumactlArg:     "retry",
+	KumactlListArg: "retries",
+}
+
 func init() {
-	registry.RegisterType(NewRetryResource())
-	registry.RegistryListType(&RetryResourceList{})
+	registry.RegisterType(RetryResourceTypeDescriptor)
 }
 
 const (
@@ -1249,10 +1327,6 @@ func NewServiceInsightResource() *ServiceInsightResource {
 	}
 }
 
-func (t *ServiceInsightResource) GetType() model.ResourceType {
-	return ServiceInsightType
-}
-
 func (t *ServiceInsightResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -1279,10 +1353,8 @@ func (t *ServiceInsightResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *ServiceInsightResource) Scope() model.ResourceScope {
-
-	return model.ScopeMesh
-
+func (t *ServiceInsightResource) Descriptor() model.ResourceTypeDescriptor {
+	return ServiceInsightResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &ServiceInsightResourceList{}
@@ -1321,9 +1393,20 @@ func (l *ServiceInsightResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var ServiceInsightResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           ServiceInsightType,
+	Resource:       NewServiceInsightResource(),
+	ResourceList:   &ServiceInsightResourceList{},
+	ReadOnly:       true,
+	AdminOnly:      false,
+	Scope:          model.ScopeMesh,
+	WsPath:         "service-insights",
+	KumactlArg:     "",
+	KumactlListArg: "",
+}
+
 func init() {
-	registry.RegisterType(NewServiceInsightResource())
-	registry.RegistryListType(&ServiceInsightResourceList{})
+	registry.RegisterType(ServiceInsightResourceTypeDescriptor)
 }
 
 const (
@@ -1343,10 +1426,6 @@ func NewServiceOverviewResource() *ServiceOverviewResource {
 	}
 }
 
-func (t *ServiceOverviewResource) GetType() model.ResourceType {
-	return ServiceOverviewType
-}
-
 func (t *ServiceOverviewResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -1373,10 +1452,8 @@ func (t *ServiceOverviewResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *ServiceOverviewResource) Scope() model.ResourceScope {
-
-	return model.ScopeMesh
-
+func (t *ServiceOverviewResource) Descriptor() model.ResourceTypeDescriptor {
+	return ServiceOverviewResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &ServiceOverviewResourceList{}
@@ -1415,6 +1492,18 @@ func (l *ServiceOverviewResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var ServiceOverviewResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           ServiceOverviewType,
+	Resource:       NewServiceOverviewResource(),
+	ResourceList:   &ServiceOverviewResourceList{},
+	ReadOnly:       false,
+	AdminOnly:      false,
+	Scope:          model.ScopeMesh,
+	WsPath:         "",
+	KumactlArg:     "",
+	KumactlListArg: "",
+}
+
 const (
 	TimeoutType model.ResourceType = "Timeout"
 )
@@ -1432,10 +1521,6 @@ func NewTimeoutResource() *TimeoutResource {
 	}
 }
 
-func (t *TimeoutResource) GetType() model.ResourceType {
-	return TimeoutType
-}
-
 func (t *TimeoutResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -1466,10 +1551,8 @@ func (t *TimeoutResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *TimeoutResource) Scope() model.ResourceScope {
-
-	return model.ScopeMesh
-
+func (t *TimeoutResource) Descriptor() model.ResourceTypeDescriptor {
+	return TimeoutResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &TimeoutResourceList{}
@@ -1508,9 +1591,21 @@ func (l *TimeoutResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var TimeoutResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           TimeoutType,
+	Resource:       NewTimeoutResource(),
+	ResourceList:   &TimeoutResourceList{},
+	ReadOnly:       false,
+	AdminOnly:      false,
+	Scope:          model.ScopeMesh,
+	KDSFlags:       model.FromGlobalToZone,
+	WsPath:         "timeouts",
+	KumactlArg:     "timeout",
+	KumactlListArg: "timeouts",
+}
+
 func init() {
-	registry.RegisterType(NewTimeoutResource())
-	registry.RegistryListType(&TimeoutResourceList{})
+	registry.RegisterType(TimeoutResourceTypeDescriptor)
 }
 
 const (
@@ -1530,10 +1625,6 @@ func NewTrafficLogResource() *TrafficLogResource {
 	}
 }
 
-func (t *TrafficLogResource) GetType() model.ResourceType {
-	return TrafficLogType
-}
-
 func (t *TrafficLogResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -1564,10 +1655,8 @@ func (t *TrafficLogResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *TrafficLogResource) Scope() model.ResourceScope {
-
-	return model.ScopeMesh
-
+func (t *TrafficLogResource) Descriptor() model.ResourceTypeDescriptor {
+	return TrafficLogResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &TrafficLogResourceList{}
@@ -1606,9 +1695,21 @@ func (l *TrafficLogResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var TrafficLogResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           TrafficLogType,
+	Resource:       NewTrafficLogResource(),
+	ResourceList:   &TrafficLogResourceList{},
+	ReadOnly:       false,
+	AdminOnly:      false,
+	Scope:          model.ScopeMesh,
+	KDSFlags:       model.FromGlobalToZone,
+	WsPath:         "traffic-logs",
+	KumactlArg:     "traffic-log",
+	KumactlListArg: "traffic-logs",
+}
+
 func init() {
-	registry.RegisterType(NewTrafficLogResource())
-	registry.RegistryListType(&TrafficLogResourceList{})
+	registry.RegisterType(TrafficLogResourceTypeDescriptor)
 }
 
 const (
@@ -1628,10 +1729,6 @@ func NewTrafficPermissionResource() *TrafficPermissionResource {
 	}
 }
 
-func (t *TrafficPermissionResource) GetType() model.ResourceType {
-	return TrafficPermissionType
-}
-
 func (t *TrafficPermissionResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -1662,10 +1759,8 @@ func (t *TrafficPermissionResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *TrafficPermissionResource) Scope() model.ResourceScope {
-
-	return model.ScopeMesh
-
+func (t *TrafficPermissionResource) Descriptor() model.ResourceTypeDescriptor {
+	return TrafficPermissionResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &TrafficPermissionResourceList{}
@@ -1704,9 +1799,21 @@ func (l *TrafficPermissionResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var TrafficPermissionResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           TrafficPermissionType,
+	Resource:       NewTrafficPermissionResource(),
+	ResourceList:   &TrafficPermissionResourceList{},
+	ReadOnly:       false,
+	AdminOnly:      false,
+	Scope:          model.ScopeMesh,
+	KDSFlags:       model.FromGlobalToZone,
+	WsPath:         "traffic-permissions",
+	KumactlArg:     "traffic-permission",
+	KumactlListArg: "traffic-permissions",
+}
+
 func init() {
-	registry.RegisterType(NewTrafficPermissionResource())
-	registry.RegistryListType(&TrafficPermissionResourceList{})
+	registry.RegisterType(TrafficPermissionResourceTypeDescriptor)
 }
 
 const (
@@ -1726,10 +1833,6 @@ func NewTrafficRouteResource() *TrafficRouteResource {
 	}
 }
 
-func (t *TrafficRouteResource) GetType() model.ResourceType {
-	return TrafficRouteType
-}
-
 func (t *TrafficRouteResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -1760,10 +1863,8 @@ func (t *TrafficRouteResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *TrafficRouteResource) Scope() model.ResourceScope {
-
-	return model.ScopeMesh
-
+func (t *TrafficRouteResource) Descriptor() model.ResourceTypeDescriptor {
+	return TrafficRouteResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &TrafficRouteResourceList{}
@@ -1802,9 +1903,21 @@ func (l *TrafficRouteResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var TrafficRouteResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           TrafficRouteType,
+	Resource:       NewTrafficRouteResource(),
+	ResourceList:   &TrafficRouteResourceList{},
+	ReadOnly:       false,
+	AdminOnly:      false,
+	Scope:          model.ScopeMesh,
+	KDSFlags:       model.FromGlobalToZone,
+	WsPath:         "traffic-routes",
+	KumactlArg:     "traffic-route",
+	KumactlListArg: "traffic-routes",
+}
+
 func init() {
-	registry.RegisterType(NewTrafficRouteResource())
-	registry.RegistryListType(&TrafficRouteResourceList{})
+	registry.RegisterType(TrafficRouteResourceTypeDescriptor)
 }
 
 const (
@@ -1824,10 +1937,6 @@ func NewTrafficTraceResource() *TrafficTraceResource {
 	}
 }
 
-func (t *TrafficTraceResource) GetType() model.ResourceType {
-	return TrafficTraceType
-}
-
 func (t *TrafficTraceResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -1854,10 +1963,8 @@ func (t *TrafficTraceResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *TrafficTraceResource) Scope() model.ResourceScope {
-
-	return model.ScopeMesh
-
+func (t *TrafficTraceResource) Descriptor() model.ResourceTypeDescriptor {
+	return TrafficTraceResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &TrafficTraceResourceList{}
@@ -1896,9 +2003,21 @@ func (l *TrafficTraceResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var TrafficTraceResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           TrafficTraceType,
+	Resource:       NewTrafficTraceResource(),
+	ResourceList:   &TrafficTraceResourceList{},
+	ReadOnly:       false,
+	AdminOnly:      false,
+	Scope:          model.ScopeMesh,
+	KDSFlags:       model.FromGlobalToZone,
+	WsPath:         "traffic-traces",
+	KumactlArg:     "traffic-trace",
+	KumactlListArg: "traffic-traces",
+}
+
 func init() {
-	registry.RegisterType(NewTrafficTraceResource())
-	registry.RegistryListType(&TrafficTraceResourceList{})
+	registry.RegisterType(TrafficTraceResourceTypeDescriptor)
 }
 
 const (
@@ -1918,10 +2037,6 @@ func NewZoneIngressResource() *ZoneIngressResource {
 	}
 }
 
-func (t *ZoneIngressResource) GetType() model.ResourceType {
-	return ZoneIngressType
-}
-
 func (t *ZoneIngressResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -1944,10 +2059,8 @@ func (t *ZoneIngressResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *ZoneIngressResource) Scope() model.ResourceScope {
-
-	return model.ScopeGlobal
-
+func (t *ZoneIngressResource) Descriptor() model.ResourceTypeDescriptor {
+	return ZoneIngressResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &ZoneIngressResourceList{}
@@ -1986,9 +2099,21 @@ func (l *ZoneIngressResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var ZoneIngressResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           ZoneIngressType,
+	Resource:       NewZoneIngressResource(),
+	ResourceList:   &ZoneIngressResourceList{},
+	ReadOnly:       false,
+	AdminOnly:      false,
+	Scope:          model.ScopeGlobal,
+	KDSFlags:       model.FromZoneToGlobal | model.FromGlobalToZone,
+	WsPath:         "zone-ingresses",
+	KumactlArg:     "zone-ingress",
+	KumactlListArg: "zone-ingresses",
+}
+
 func init() {
-	registry.RegisterType(NewZoneIngressResource())
-	registry.RegistryListType(&ZoneIngressResourceList{})
+	registry.RegisterType(ZoneIngressResourceTypeDescriptor)
 }
 
 const (
@@ -2008,10 +2133,6 @@ func NewZoneIngressInsightResource() *ZoneIngressInsightResource {
 	}
 }
 
-func (t *ZoneIngressInsightResource) GetType() model.ResourceType {
-	return ZoneIngressInsightType
-}
-
 func (t *ZoneIngressInsightResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -2038,10 +2159,8 @@ func (t *ZoneIngressInsightResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *ZoneIngressInsightResource) Scope() model.ResourceScope {
-
-	return model.ScopeGlobal
-
+func (t *ZoneIngressInsightResource) Descriptor() model.ResourceTypeDescriptor {
+	return ZoneIngressInsightResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &ZoneIngressInsightResourceList{}
@@ -2080,9 +2199,21 @@ func (l *ZoneIngressInsightResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var ZoneIngressInsightResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           ZoneIngressInsightType,
+	Resource:       NewZoneIngressInsightResource(),
+	ResourceList:   &ZoneIngressInsightResourceList{},
+	ReadOnly:       true,
+	AdminOnly:      false,
+	Scope:          model.ScopeGlobal,
+	KDSFlags:       model.FromZoneToGlobal,
+	WsPath:         "zone-ingress-insights",
+	KumactlArg:     "",
+	KumactlListArg: "",
+}
+
 func init() {
-	registry.RegisterType(NewZoneIngressInsightResource())
-	registry.RegistryListType(&ZoneIngressInsightResourceList{})
+	registry.RegisterType(ZoneIngressInsightResourceTypeDescriptor)
 }
 
 const (
@@ -2102,10 +2233,6 @@ func NewZoneIngressOverviewResource() *ZoneIngressOverviewResource {
 	}
 }
 
-func (t *ZoneIngressOverviewResource) GetType() model.ResourceType {
-	return ZoneIngressOverviewType
-}
-
 func (t *ZoneIngressOverviewResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -2132,10 +2259,8 @@ func (t *ZoneIngressOverviewResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *ZoneIngressOverviewResource) Scope() model.ResourceScope {
-
-	return model.ScopeGlobal
-
+func (t *ZoneIngressOverviewResource) Descriptor() model.ResourceTypeDescriptor {
+	return ZoneIngressOverviewResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &ZoneIngressOverviewResourceList{}
@@ -2173,3 +2298,15 @@ func (l *ZoneIngressOverviewResourceList) AddItem(r model.Resource) error {
 func (l *ZoneIngressOverviewResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
+
+var ZoneIngressOverviewResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           ZoneIngressOverviewType,
+	Resource:       NewZoneIngressOverviewResource(),
+	ResourceList:   &ZoneIngressOverviewResourceList{},
+	ReadOnly:       false,
+	AdminOnly:      false,
+	Scope:          model.ScopeGlobal,
+	WsPath:         "",
+	KumactlArg:     "",
+	KumactlListArg: "",
+}
diff --git a/pkg/core/resources/apis/system/generated_resources.go b/pkg/core/resources/apis/system/generated_resources.go
index 8ad5401ff..982fcf968 100644
--- a/pkg/core/resources/apis/system/generated_resources.go
+++ b/pkg/core/resources/apis/system/generated_resources.go
@@ -29,10 +29,6 @@ func NewConfigResource() *ConfigResource {
 	}
 }
 
-func (t *ConfigResource) GetType() model.ResourceType {
-	return ConfigType
-}
-
 func (t *ConfigResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -59,10 +55,8 @@ func (t *ConfigResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *ConfigResource) Scope() model.ResourceScope {
-
-	return model.ScopeGlobal
-
+func (t *ConfigResource) Descriptor() model.ResourceTypeDescriptor {
+	return ConfigResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &ConfigResourceList{}
@@ -101,9 +95,21 @@ func (l *ConfigResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var ConfigResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           ConfigType,
+	Resource:       NewConfigResource(),
+	ResourceList:   &ConfigResourceList{},
+	ReadOnly:       false,
+	AdminOnly:      false,
+	Scope:          model.ScopeGlobal,
+	KDSFlags:       model.FromGlobalToZone,
+	WsPath:         "",
+	KumactlArg:     "",
+	KumactlListArg: "",
+}
+
 func init() {
-	registry.RegisterType(NewConfigResource())
-	registry.RegistryListType(&ConfigResourceList{})
+	registry.RegisterType(ConfigResourceTypeDescriptor)
 }
 
 const (
@@ -123,10 +129,6 @@ func NewSecretResource() *SecretResource {
 	}
 }
 
-func (t *SecretResource) GetType() model.ResourceType {
-	return SecretType
-}
-
 func (t *SecretResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -153,10 +155,8 @@ func (t *SecretResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *SecretResource) Scope() model.ResourceScope {
-
-	return model.ScopeMesh
-
+func (t *SecretResource) Descriptor() model.ResourceTypeDescriptor {
+	return SecretResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &SecretResourceList{}
@@ -195,9 +195,21 @@ func (l *SecretResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var SecretResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           SecretType,
+	Resource:       NewSecretResource(),
+	ResourceList:   &SecretResourceList{},
+	ReadOnly:       false,
+	AdminOnly:      true,
+	Scope:          model.ScopeMesh,
+	KDSFlags:       model.FromGlobalToZone,
+	WsPath:         "secrets",
+	KumactlArg:     "secret",
+	KumactlListArg: "secrets",
+}
+
 func init() {
-	registry.RegisterType(NewSecretResource())
-	registry.RegistryListType(&SecretResourceList{})
+	registry.RegisterType(SecretResourceTypeDescriptor)
 }
 
 const (
@@ -217,10 +229,6 @@ func NewZoneResource() *ZoneResource {
 	}
 }
 
-func (t *ZoneResource) GetType() model.ResourceType {
-	return ZoneType
-}
-
 func (t *ZoneResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -247,10 +255,8 @@ func (t *ZoneResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *ZoneResource) Scope() model.ResourceScope {
-
-	return model.ScopeGlobal
-
+func (t *ZoneResource) Descriptor() model.ResourceTypeDescriptor {
+	return ZoneResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &ZoneResourceList{}
@@ -289,9 +295,20 @@ func (l *ZoneResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var ZoneResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           ZoneType,
+	Resource:       NewZoneResource(),
+	ResourceList:   &ZoneResourceList{},
+	ReadOnly:       false,
+	AdminOnly:      false,
+	Scope:          model.ScopeGlobal,
+	WsPath:         "zones",
+	KumactlArg:     "zone",
+	KumactlListArg: "zones",
+}
+
 func init() {
-	registry.RegisterType(NewZoneResource())
-	registry.RegistryListType(&ZoneResourceList{})
+	registry.RegisterType(ZoneResourceTypeDescriptor)
 }
 
 const (
@@ -311,10 +328,6 @@ func NewZoneInsightResource() *ZoneInsightResource {
 	}
 }
 
-func (t *ZoneInsightResource) GetType() model.ResourceType {
-	return ZoneInsightType
-}
-
 func (t *ZoneInsightResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -341,10 +354,8 @@ func (t *ZoneInsightResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *ZoneInsightResource) Scope() model.ResourceScope {
-
-	return model.ScopeGlobal
-
+func (t *ZoneInsightResource) Descriptor() model.ResourceTypeDescriptor {
+	return ZoneInsightResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &ZoneInsightResourceList{}
@@ -383,9 +394,20 @@ func (l *ZoneInsightResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var ZoneInsightResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           ZoneInsightType,
+	Resource:       NewZoneInsightResource(),
+	ResourceList:   &ZoneInsightResourceList{},
+	ReadOnly:       true,
+	AdminOnly:      false,
+	Scope:          model.ScopeGlobal,
+	WsPath:         "zone-insights",
+	KumactlArg:     "",
+	KumactlListArg: "",
+}
+
 func init() {
-	registry.RegisterType(NewZoneInsightResource())
-	registry.RegistryListType(&ZoneInsightResourceList{})
+	registry.RegisterType(ZoneInsightResourceTypeDescriptor)
 }
 
 const (
@@ -405,10 +427,6 @@ func NewZoneOverviewResource() *ZoneOverviewResource {
 	}
 }
 
-func (t *ZoneOverviewResource) GetType() model.ResourceType {
-	return ZoneOverviewType
-}
-
 func (t *ZoneOverviewResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -435,10 +453,8 @@ func (t *ZoneOverviewResource) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *ZoneOverviewResource) Scope() model.ResourceScope {
-
-	return model.ScopeGlobal
-
+func (t *ZoneOverviewResource) Descriptor() model.ResourceTypeDescriptor {
+	return ZoneOverviewResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &ZoneOverviewResourceList{}
@@ -476,3 +492,15 @@ func (l *ZoneOverviewResourceList) AddItem(r model.Resource) error {
 func (l *ZoneOverviewResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
+
+var ZoneOverviewResourceTypeDescriptor = model.ResourceTypeDescriptor{
+	Name:           ZoneOverviewType,
+	Resource:       NewZoneOverviewResource(),
+	ResourceList:   &ZoneOverviewResourceList{},
+	ReadOnly:       false,
+	AdminOnly:      false,
+	Scope:          model.ScopeGlobal,
+	WsPath:         "",
+	KumactlArg:     "",
+	KumactlListArg: "",
+}
diff --git a/pkg/core/resources/apis/system/global_secret.go b/pkg/core/resources/apis/system/global_secret.go
index d578332fa..cdf6bce18 100644
--- a/pkg/core/resources/apis/system/global_secret.go
+++ b/pkg/core/resources/apis/system/global_secret.go
@@ -25,10 +25,6 @@ func NewGlobalSecretResource() *GlobalSecretResource {
 	}
 }
 
-func (t *GlobalSecretResource) GetType() model.ResourceType {
-	return GlobalSecretType
-}
-
 func (t *GlobalSecretResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -55,8 +51,8 @@ func (t *GlobalSecretResource) Validate() error {
 	return nil
 }
 
-func (t *GlobalSecretResource) Scope() model.ResourceScope {
-	return model.ScopeGlobal
+func (t *GlobalSecretResource) Descriptor() model.ResourceTypeDescriptor {
+	return GlobalSecretResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &GlobalSecretResourceList{}
@@ -95,7 +91,20 @@ func (l *GlobalSecretResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var GlobalSecretResourceTypeDescriptor model.ResourceTypeDescriptor
+
 func init() {
-	registry.RegisterType(NewGlobalSecretResource())
-	registry.RegistryListType(&GlobalSecretResourceList{})
+	GlobalSecretResourceTypeDescriptor = model.ResourceTypeDescriptor{
+		Name:           GlobalSecretType,
+		Resource:       NewGlobalSecretResource(),
+		ResourceList:   &GlobalSecretResourceList{},
+		ReadOnly:       false,
+		AdminOnly:      true,
+		Scope:          model.ScopeGlobal,
+		KDSFlags:       model.FromGlobalToZone,
+		WsPath:         "global-secrets",
+		KumactlArg:     "global-secret",
+		KumactlListArg: "global-secrets",
+	}
+	registry.RegisterType(GlobalSecretResourceTypeDescriptor)
 }
diff --git a/pkg/core/resources/manager/cache.go b/pkg/core/resources/manager/cache.go
index 79213dda5..1c37f8dde 100644
--- a/pkg/core/resources/manager/cache.go
+++ b/pkg/core/resources/manager/cache.go
@@ -48,7 +48,7 @@ func NewCachedManager(delegate ReadOnlyResourceManager, expirationTime time.Dura
 
 func (c *cachedManager) Get(ctx context.Context, res model.Resource, fs ...store.GetOptionsFunc) error {
 	opts := store.NewGetOptions(fs...)
-	cacheKey := fmt.Sprintf("GET:%s:%s", res.GetType(), opts.HashCode())
+	cacheKey := fmt.Sprintf("GET:%s:%s", res.Descriptor().Name, opts.HashCode())
 	obj, found := c.cache.Get(cacheKey)
 	if !found {
 		// There might be a situation when cache just expired and there are many concurrent goroutines here.
@@ -58,19 +58,19 @@ func (c *cachedManager) Get(ctx context.Context, res model.Resource, fs ...store
 		obj, found = c.cache.Get(cacheKey)
 		if !found {
 			// After many goroutines are unlocked one by one, only one should execute this branch, the rest should retrieve object from the cache
-			c.metrics.WithLabelValues("get", string(res.GetType()), "miss").Inc()
+			c.metrics.WithLabelValues("get", string(res.Descriptor().Name), "miss").Inc()
 			if err := c.delegate.Get(ctx, res, fs...); err != nil {
 				mutex.Unlock()
 				return err
 			}
 			c.cache.SetDefault(cacheKey, res)
 		} else {
-			c.metrics.WithLabelValues("get", string(res.GetType()), "hit-wait").Inc()
+			c.metrics.WithLabelValues("get", string(res.Descriptor().Name), "hit-wait").Inc()
 		}
 		mutex.Unlock()
 		c.cleanMutexFor(cacheKey) // We need to cleanup mutexes from the map, otherwise we can see the memory leak.
 	} else {
-		c.metrics.WithLabelValues("get", string(res.GetType()), "hit").Inc()
+		c.metrics.WithLabelValues("get", string(res.Descriptor().Name), "hit").Inc()
 	}
 
 	if found {
diff --git a/pkg/core/resources/manager/customizable_manager.go b/pkg/core/resources/manager/customizable_manager.go
index 60ac8c3b4..a46db9c22 100644
--- a/pkg/core/resources/manager/customizable_manager.go
+++ b/pkg/core/resources/manager/customizable_manager.go
@@ -37,7 +37,7 @@ func (m *customizableResourceManager) Customize(resourceType model.ResourceType,
 }
 
 func (m *customizableResourceManager) Get(ctx context.Context, resource model.Resource, fs ...store.GetOptionsFunc) error {
-	return m.ResourceManager(resource.GetType()).Get(ctx, resource, fs...)
+	return m.ResourceManager(resource.Descriptor().Name).Get(ctx, resource, fs...)
 }
 
 func (m *customizableResourceManager) List(ctx context.Context, list model.ResourceList, fs ...store.ListOptionsFunc) error {
@@ -45,11 +45,11 @@ func (m *customizableResourceManager) List(ctx context.Context, list model.Resou
 }
 
 func (m *customizableResourceManager) Create(ctx context.Context, resource model.Resource, fs ...store.CreateOptionsFunc) error {
-	return m.ResourceManager(resource.GetType()).Create(ctx, resource, fs...)
+	return m.ResourceManager(resource.Descriptor().Name).Create(ctx, resource, fs...)
 }
 
 func (m *customizableResourceManager) Delete(ctx context.Context, resource model.Resource, fs ...store.DeleteOptionsFunc) error {
-	return m.ResourceManager(resource.GetType()).Delete(ctx, resource, fs...)
+	return m.ResourceManager(resource.Descriptor().Name).Delete(ctx, resource, fs...)
 }
 
 func (m *customizableResourceManager) DeleteAll(ctx context.Context, list model.ResourceList, fs ...store.DeleteAllOptionsFunc) error {
@@ -57,7 +57,7 @@ func (m *customizableResourceManager) DeleteAll(ctx context.Context, list model.
 }
 
 func (m *customizableResourceManager) Update(ctx context.Context, resource model.Resource, fs ...store.UpdateOptionsFunc) error {
-	return m.ResourceManager(resource.GetType()).Update(ctx, resource, fs...)
+	return m.ResourceManager(resource.Descriptor().Name).Update(ctx, resource, fs...)
 }
 
 func (m *customizableResourceManager) ResourceManager(typ model.ResourceType) ResourceManager {
diff --git a/pkg/core/resources/manager/manager.go b/pkg/core/resources/manager/manager.go
index 886f666dd..859b68d27 100644
--- a/pkg/core/resources/manager/manager.go
+++ b/pkg/core/resources/manager/manager.go
@@ -53,13 +53,13 @@ func (r *resourcesManager) Create(ctx context.Context, resource model.Resource,
 	opts := store.NewCreateOptions(fs...)
 
 	var owner model.Resource
-	if resource.Scope() == model.ScopeMesh {
+	if resource.Descriptor().Scope == model.ScopeMesh {
 		owner = core_mesh.NewMeshResource()
 		if err := r.Store.Get(ctx, owner, store.GetByKey(opts.Mesh, model.NoMesh)); err != nil {
 			return MeshNotFound(opts.Mesh)
 		}
 	}
-	if resource.GetType() == core_mesh.MeshInsightType {
+	if resource.Descriptor().Name == core_mesh.MeshInsightType {
 		owner = core_mesh.NewMeshResource()
 		if err := r.Store.Get(ctx, owner, store.GetByKey(opts.Name, model.NoMesh)); err != nil {
 			return MeshNotFound(opts.Name)
diff --git a/pkg/core/resources/model/resource.go b/pkg/core/resources/model/resource.go
index f68adb4e7..3dedbb947 100644
--- a/pkg/core/resources/model/resource.go
+++ b/pkg/core/resources/model/resource.go
@@ -6,6 +6,7 @@ import (
 	"time"
 
 	"github.com/golang/protobuf/proto"
+	"github.com/pkg/errors"
 )
 
 const (
@@ -33,14 +34,133 @@ const (
 	ScopeGlobal = "Global"
 )
 
+type KDSFlagType uint32
+
+const (
+	ConsumedByZone   = KDSFlagType(1)
+	ConsumedByGlobal = KDSFlagType(1 << 2)
+	ProvidedByZone   = KDSFlagType(1 << 3)
+	ProvidedByGlobal = KDSFlagType(1 << 4)
+	FromZoneToGlobal = ConsumedByGlobal | ProvidedByZone
+	FromGlobalToZone = ProvidedByGlobal | ConsumedByZone
+)
+
+// Has return whether this flag has all the passed flags on.
+func (kt KDSFlagType) Has(flag KDSFlagType) bool {
+	return kt&flag != 0
+}
+
 type Resource interface {
-	GetType() ResourceType
 	GetMeta() ResourceMeta
 	SetMeta(ResourceMeta)
 	GetSpec() ResourceSpec
 	SetSpec(ResourceSpec) error
 	Validate() error
-	Scope() ResourceScope
+	Descriptor() ResourceTypeDescriptor
+}
+
+func InitDescriptor(res ResourceTypeDescriptor) ResourceTypeDescriptor {
+	newType := reflect.TypeOf(res.Resource).Elem()
+	res.objectType = newType
+	if res.ResourceList != nil {
+		res.listType = reflect.TypeOf(res.ResourceList).Elem()
+	}
+	return res
+}
+
+type ResourceTypeDescriptor struct {
+	// Name identifier of this resourceType this maps to the k8s entity and universal name.
+	Name ResourceType
+	// Resource a created element of this type
+	Resource Resource
+	// ResourceList a create list container of this type
+	ResourceList ResourceList
+	// ReadOnly if this type will be created, modified and deleted by the system.
+	ReadOnly bool
+	// AdminOnly if this type requires users to be admin to access.
+	AdminOnly bool
+	// Scope whether this resource is Global or Mesh scoped.
+	Scope ResourceScope
+	// KDSFlags a set of flags that defines how this entity is sent using KDS (if unset KDS is disabled).
+	KDSFlags KDSFlagType
+	// WsPath the path to access on the REST api.
+	WsPath string
+	// KumactlArg the name of the cmdline argument when doing `get` or `delete`.
+	KumactlArg string
+	// KumactlListArg the name of the cmdline argument when doing `list`.
+	KumactlListArg string
+	objectType     reflect.Type
+	listType       reflect.Type
+}
+
+func (d ResourceTypeDescriptor) NewObject() Resource {
+	newSpec := proto.Clone(d.Resource.GetSpec())
+	resource := reflect.New(d.objectType).Interface().(Resource)
+	if err := resource.SetSpec(newSpec); err != nil {
+		panic(errors.Wrap(err, "could not set spec on the new resource"))
+	}
+	return resource
+}
+
+func (d ResourceTypeDescriptor) NewList() ResourceList {
+	return reflect.New(d.listType).Interface().(ResourceList)
+}
+
+type TypeFilter interface {
+	Apply(descriptor ResourceTypeDescriptor) bool
+}
+
+type TypeFilterFn func(descriptor ResourceTypeDescriptor) bool
+
+func (f TypeFilterFn) Apply(descriptor ResourceTypeDescriptor) bool {
+	return f(descriptor)
+}
+
+func HasKDSFlag(flagType KDSFlagType) TypeFilter {
+	return TypeFilterFn(func(descriptor ResourceTypeDescriptor) bool {
+		return descriptor.KDSFlags.Has(flagType)
+	})
+}
+
+func HasKdsEnabled() TypeFilter {
+	return TypeFilterFn(func(descriptor ResourceTypeDescriptor) bool {
+		return descriptor.KDSFlags != 0
+	})
+}
+
+func HasKumactlEnabled() TypeFilter {
+	return TypeFilterFn(func(descriptor ResourceTypeDescriptor) bool {
+		return descriptor.KumactlArg != ""
+	})
+}
+
+func HasWsEnabled() TypeFilter {
+	return TypeFilterFn(func(descriptor ResourceTypeDescriptor) bool {
+		return descriptor.WsPath != ""
+	})
+}
+
+func HasScope(scope ResourceScope) TypeFilter {
+	return TypeFilterFn(func(descriptor ResourceTypeDescriptor) bool {
+		return descriptor.Scope == scope
+	})
+}
+
+func Named(names ...ResourceType) TypeFilter {
+	included := map[ResourceType]bool{}
+	for _, n := range names {
+		included[n] = true
+	}
+
+	return TypeFilterFn(func(descriptor ResourceTypeDescriptor) bool {
+		return included[descriptor.Name]
+	})
+}
+
+func Not(filter TypeFilter) TypeFilter {
+	return TypeFilterFn(func(descriptor ResourceTypeDescriptor) bool {
+		return !filter.Apply(descriptor)
+	})
 }
 
 type ByMeta []Resource
diff --git a/pkg/core/resources/model/rest/api.go b/pkg/core/resources/model/rest/api.go
index 61feb8e21..f9349f571 100644
--- a/pkg/core/resources/model/rest/api.go
+++ b/pkg/core/resources/model/rest/api.go
@@ -6,7 +6,6 @@ import (
 	"github.com/pkg/errors"
 
 	"github.com/kumahq/kuma/pkg/core/resources/model"
-	"github.com/kumahq/kuma/pkg/core/resources/registry"
 )
 
 type Api interface {
@@ -18,12 +17,14 @@ type ResourceApi interface {
 	Item(mesh string, name string) string
 }
 
-func NewResourceApi(resType model.ResourceType, path string) ResourceApi {
-	res, _ := registry.Global().NewObject(resType)
-	if res.Scope() == model.ScopeGlobal {
+func NewResourceApi(scope model.ResourceScope, path string) ResourceApi {
+	switch scope {
+	case model.ScopeGlobal:
 		return &nonMeshedApi{CollectionPath: path}
-	} else {
+	case model.ScopeMesh:
 		return &meshedApi{CollectionPath: path}
+	default:
+		panic("Unsupported scope type")
 	}
 }
 
diff --git a/pkg/core/resources/model/rest/converter.go b/pkg/core/resources/model/rest/converter.go
index 8d5daabab..8bf1631e6 100644
--- a/pkg/core/resources/model/rest/converter.go
+++ b/pkg/core/resources/model/rest/converter.go
@@ -11,13 +11,13 @@ type from struct{}
 
 func (c *from) Resource(r model.Resource) *Resource {
 	var meshName string
-	if r.Scope() == model.ScopeMesh {
+	if r.Descriptor().Scope == model.ScopeMesh {
 		meshName = r.GetMeta().GetMesh()
 	}
 	return &Resource{
 		Meta: ResourceMeta{
 			Mesh:             meshName,
-			Type:             string(r.GetType()),
+			Type:             string(r.Descriptor().Name),
 			Name:             r.GetMeta().GetName(),
 			CreationTime:     r.GetMeta().GetCreationTime(),
 			ModificationTime: r.GetMeta().GetModificationTime(),
diff --git a/pkg/core/resources/model/rest/resource.go b/pkg/core/resources/model/rest/resource.go
index 311bf6a76..1f6d87cb4 100644
--- a/pkg/core/resources/model/rest/resource.go
+++ b/pkg/core/resources/model/rest/resource.go
@@ -59,7 +59,7 @@ func NewFromModel(m model.Resource) *Resource {
 	meta := m.GetMeta()
 	return &Resource{
 		Meta: ResourceMeta{
-			Type:             string(m.GetType()),
+			Type:             string(m.Descriptor().Name),
 			Mesh:             meta.GetMesh(),
 			Name:             meta.GetName(),
 			CreationTime:     meta.GetCreationTime(),
diff --git a/pkg/core/resources/registry/global.go b/pkg/core/resources/registry/global.go
index 6bd92d658..2ac30dae9 100644
--- a/pkg/core/resources/registry/global.go
+++ b/pkg/core/resources/registry/global.go
@@ -8,14 +8,8 @@ func Global() TypeRegistry {
 	return global
 }
 
-func RegisterType(res model.Resource) {
+func RegisterType(res model.ResourceTypeDescriptor) {
 	if err := global.RegisterType(res); err != nil {
 		panic(err)
 	}
 }
-
-func RegistryListType(resList model.ResourceList) {
-	if err := global.RegisterListType(resList); err != nil {
-		panic(err)
-	}
-}
diff --git a/pkg/core/resources/registry/registry.go b/pkg/core/resources/registry/registry.go
index 9ffac0834..9de7cc487 100644
--- a/pkg/core/resources/registry/registry.go
+++ b/pkg/core/resources/registry/registry.go
@@ -3,93 +3,91 @@ package registry
 import (
 	"reflect"
 
-	"github.com/golang/protobuf/proto"
 	"github.com/pkg/errors"
 
 	"github.com/kumahq/kuma/pkg/core/resources/model"
 )
 
 type TypeRegistry interface {
-	RegisterType(model.Resource) error
-	RegisterListType(model.ResourceList) error
+	RegisterType(model.ResourceTypeDescriptor) error
 
 	NewObject(model.ResourceType) (model.Resource, error)
 	NewList(model.ResourceType) (model.ResourceList, error)
+	DescriptorFor(resourceType model.ResourceType) (model.ResourceTypeDescriptor, error)
 
-	ObjectTypes() []model.ResourceType
-	ListTypes() []model.ResourceType
+	ObjectTypes(filters ...model.TypeFilter) []model.ResourceType
+	ObjectDescriptors(filters ...model.TypeFilter) []model.ResourceTypeDescriptor
 }
 
 func NewTypeRegistry() TypeRegistry {
 	return &typeRegistry{
-		objectTypes:     make(map[model.ResourceType]reflect.Type),
-		objectSpecs:     make(map[model.ResourceType]model.ResourceSpec),
-		objectListTypes: make(map[model.ResourceType]reflect.Type),
+		descriptors: make(map[model.ResourceType]model.ResourceTypeDescriptor),
 	}
 }
 
 type typeRegistry struct {
-	objectSpecs     map[model.ResourceType]model.ResourceSpec
-	objectTypes     map[model.ResourceType]reflect.Type
-	objectListTypes map[model.ResourceType]reflect.Type
+	descriptors map[model.ResourceType]model.ResourceTypeDescriptor
 }
 
-func (t *typeRegistry) ObjectTypes() []model.ResourceType {
-	var types []model.ResourceType
-	for typ := range t.objectTypes {
-		types = append(types, typ)
+func (t *typeRegistry) DescriptorFor(resType model.ResourceType) (model.ResourceTypeDescriptor, error) {
+	typDesc, ok := t.descriptors[resType]
+	if !ok {
+		return model.ResourceTypeDescriptor{}, errors.Errorf("invalid resource type %q", resType)
 	}
-	return types
+	return typDesc, nil
+}
+
+func (t *typeRegistry) ObjectDescriptors(filters ...model.TypeFilter) []model.ResourceTypeDescriptor {
+	var descriptors []model.ResourceTypeDescriptor
+	for _, typ := range t.descriptors {
+		match := true
+		for _, f := range filters {
+			match = match && f.Apply(typ)
+		}
+		if match {
+			descriptors = append(descriptors, typ)
+		}
+	}
+	return descriptors
 }
 
-func (t *typeRegistry) ListTypes() []model.ResourceType {
+func (t *typeRegistry) ObjectTypes(filters ...model.TypeFilter) []model.ResourceType {
 	var types []model.ResourceType
-	for typ := range t.objectListTypes {
-		types = append(types, typ)
+	for _, typ := range t.descriptors {
+		match := true
+		for _, f := range filters {
+			match = match && f.Apply(typ)
+		}
+		if match {
+			types = append(types, typ.Name)
+		}
 	}
 	return types
 }
 
-func (t *typeRegistry) RegisterType(res model.Resource) error {
-	newType := reflect.TypeOf(res).Elem()
-	if previous, ok := t.objectTypes[res.GetType()]; ok {
-		return errors.Errorf("duplicate registration of ResourceType under name %q: previous=%#v new=%#v", res.GetType(), previous.String(), newType.String())
-	}
-	if res.GetSpec() == nil {
+func (t *typeRegistry) RegisterType(res model.ResourceTypeDescriptor) error {
+	if res.Resource.GetSpec() == nil {
 		return errors.New("spec in the object cannot be nil")
 	}
-	newSpec := proto.Clone(res.GetSpec())
-	t.objectSpecs[res.GetType()] = newSpec
-	t.objectTypes[res.GetType()] = newType
-	return nil
-}
-
-func (t *typeRegistry) RegisterListType(resList model.ResourceList) error {
-	newType := reflect.TypeOf(resList).Elem()
-	if previous, ok := t.objectListTypes[resList.GetItemType()]; ok {
-		return errors.Errorf("duplicate registration of ResourceType under name %q: previous=%#v new=%#v", resList.GetItemType(), previous.String(), newType.String())
+	if previous, ok := t.descriptors[res.Name]; ok {
+		return errors.Errorf("duplicate registration of ResourceType under name %q: previous=%#v new=%#v", res.Name, previous, reflect.TypeOf(res.Resource).Elem().String())
 	}
-	t.objectListTypes[resList.GetItemType()] = reflect.TypeOf(resList).Elem()
+	t.descriptors[res.Name] = model.InitDescriptor(res)
 	return nil
 }
 
 func (t *typeRegistry) NewObject(resType model.ResourceType) (model.Resource, error) {
-	typ, ok := t.objectTypes[resType]
+	typDesc, ok := t.descriptors[resType]
 	if !ok {
 		return nil, errors.Errorf("invalid resource type %q", resType)
 	}
-	newSpec := proto.Clone(t.objectSpecs[resType])
-	resource := reflect.New(typ).Interface().(model.Resource)
-	if err := resource.SetSpec(newSpec); err != nil {
-		return nil, errors.Wrap(err, "could not set spec on the new resource")
-	}
-	return resource, nil
+	return typDesc.NewObject(), nil
 }
 
 func (t *typeRegistry) NewList(resType model.ResourceType) (model.ResourceList, error) {
-	typ, ok := t.objectListTypes[resType]
+	typDesc, ok := t.descriptors[resType]
 	if !ok {
 		return nil, errors.Errorf("invalid resource type %q", resType)
 	}
-	return reflect.New(typ).Interface().(model.ResourceList), nil
+	return typDesc.NewList(), nil
 }
diff --git a/pkg/core/resources/store/customizable_store.go b/pkg/core/resources/store/customizable_store.go
index ac9cb210e..343dcb26a 100644
--- a/pkg/core/resources/store/customizable_store.go
+++ b/pkg/core/resources/store/customizable_store.go
@@ -19,7 +19,7 @@ type customizableResourceStore struct {
 }
 
 func (m *customizableResourceStore) Get(ctx context.Context, resource model.Resource, fs ...GetOptionsFunc) error {
-	return m.ResourceStore(resource.GetType()).Get(ctx, resource, fs...)
+	return m.ResourceStore(resource.Descriptor().Name).Get(ctx, resource, fs...)
 }
 
 func (m *customizableResourceStore) List(ctx context.Context, list model.ResourceList, fs ...ListOptionsFunc) error {
@@ -27,15 +27,15 @@ func (m *customizableResourceStore) List(ctx context.Context, list model.Resourc
 }
 
 func (m *customizableResourceStore) Create(ctx context.Context, resource model.Resource, fs ...CreateOptionsFunc) error {
-	return m.ResourceStore(resource.GetType()).Create(ctx, resource, fs...)
+	return m.ResourceStore(resource.Descriptor().Name).Create(ctx, resource, fs...)
 }
 
 func (m *customizableResourceStore) Delete(ctx context.Context, resource model.Resource, fs ...DeleteOptionsFunc) error {
-	return m.ResourceStore(resource.GetType()).Delete(ctx, resource, fs...)
+	return m.ResourceStore(resource.Descriptor().Name).Delete(ctx, resource, fs...)
 }
 
 func (m *customizableResourceStore) Update(ctx context.Context, resource model.Resource, fs ...UpdateOptionsFunc) error {
-	return m.ResourceStore(resource.GetType()).Update(ctx, resource, fs...)
+	return m.ResourceStore(resource.Descriptor().Name).Update(ctx, resource, fs...)
 }
 
 func (m *customizableResourceStore) ResourceStore(typ model.ResourceType) ResourceStore {
diff --git a/pkg/core/resources/store/store.go b/pkg/core/resources/store/store.go
index 390f66be9..4e10cc873 100644
--- a/pkg/core/resources/store/store.go
+++ b/pkg/core/resources/store/store.go
@@ -46,7 +46,7 @@ func (s *strictResourceStore) Create(ctx context.Context, r model.Resource, fs .
 	if opts.Name == "" {
 		return fmt.Errorf("ResourceStore.Create() requires options.Name to be a non-empty value")
 	}
-	if r.Scope() == model.ScopeMesh && opts.Mesh == "" {
+	if r.Descriptor().Scope == model.ScopeMesh && opts.Mesh == "" {
 		return fmt.Errorf("ResourceStore.Create() requires options.Mesh to be a non-empty value")
 	}
 	return s.delegate.Create(ctx, r, fs...)
@@ -70,7 +70,7 @@ func (s *strictResourceStore) Delete(ctx context.Context, r model.Resource, fs .
 	if opts.Name == "" {
 		return fmt.Errorf("ResourceStore.Delete() requires options.Name to be a non-empty value")
 	}
-	if r.Scope() == model.ScopeMesh && opts.Mesh == "" {
+	if r.Descriptor().Scope == model.ScopeMesh && opts.Mesh == "" {
 		return fmt.Errorf("ResourceStore.Delete() requires options.Mesh to be a non-empty value")
 	}
 	if r.GetMeta() != nil {
@@ -95,7 +95,7 @@ func (s *strictResourceStore) Get(ctx context.Context, r model.Resource, fs ...G
 	if opts.Name == "" {
 		return fmt.Errorf("ResourceStore.Get() requires options.Name to be a non-empty value")
 	}
-	if r.Scope() == model.ScopeMesh && opts.Mesh == "" {
+	if r.Descriptor().Scope == model.ScopeMesh && opts.Mesh == "" {
 		return fmt.Errorf("ResourceStore.Get() requires options.Mesh to be a non-empty value")
 	}
 	return s.delegate.Get(ctx, r, fs...)
diff --git a/pkg/core/xds/metadata.go b/pkg/core/xds/metadata.go
index 8eb43cada..5ad9b791d 100644
--- a/pkg/core/xds/metadata.go
+++ b/pkg/core/xds/metadata.go
@@ -151,7 +151,7 @@ func DataplaneMetadataFromXdsMetadata(xdsMetadata *structpb.Struct) *DataplaneMe
 			metadata.Resource = r
 		default:
 			metadataLog.Error(err, "invalid dataplane resource type",
-				"resource", r.GetType(),
+				"resource", r.Descriptor().Name,
 				"field", fieldDataplaneDataplaneResource,
 				"value", value)
 		}
diff --git a/pkg/insights/components.go b/pkg/insights/components.go
index df022bc86..f03b8e9b7 100644
--- a/pkg/insights/components.go
+++ b/pkg/insights/components.go
@@ -4,6 +4,7 @@ import (
 	"github.com/go-kit/kit/ratelimit"
 	"golang.org/x/time/rate"
 
+	"github.com/kumahq/kuma/pkg/core/resources/registry"
 	"github.com/kumahq/kuma/pkg/core/runtime"
 	"github.com/kumahq/kuma/pkg/core/runtime/component"
 )
@@ -17,6 +18,7 @@ func Setup(rt runtime.Runtime) error {
 		RateLimiterFactory: func() ratelimit.Allower {
 			return rate.NewLimiter(rate.Every(rt.Config().Metrics.Mesh.MinResyncTimeout), 50)
 		},
+		Registry: registry.Global(),
 	})
 	return rt.Add(component.NewResilientComponent(log, resyncer))
 }
diff --git a/pkg/insights/resyncer.go b/pkg/insights/resyncer.go
index cace8ad56..92c7273d1 100644
--- a/pkg/insights/resyncer.go
+++ b/pkg/insights/resyncer.go
@@ -30,6 +30,7 @@ func ServiceInsightName(mesh string) string {
 }
 
 type Config struct {
+	Registry           registry.TypeRegistry
 	ResourceManager    manager.ResourceManager
 	EventReaderFactory events.ListenerFactory
 	MinResyncTimeout   time.Duration
@@ -48,6 +49,7 @@ type resyncer struct {
 	meshInsightMux     sync.Mutex
 	serviceInsightMux  sync.Mutex
 	rateLimiters       map[string]ratelimit.Allower
+	registry           registry.TypeRegistry
 }
 
 // NewResyncer creates a new Component that periodically updates insights
@@ -66,6 +68,7 @@ func NewResyncer(config *Config) component.Component {
 		rm:                 config.ResourceManager,
 		rateLimiterFactory: config.RateLimiterFactory,
 		rateLimiters:       map[string]ratelimit.Allower{},
+		registry:           config.Registry,
 	}
 
 	r.tick = config.Tick
@@ -108,6 +111,10 @@ func (r *resyncer) Start(stop <-chan struct{}) error {
 		if !ok {
 			continue
 		}
+		desc, err := r.registry.DescriptorFor(resourceChanged.Type)
+		if err != nil {
+			log.Error(err, "Resource is not registered in the registry, ignoring it", "resource", resourceChanged.Type)
+		}
 		if resourceChanged.Type == core_mesh.MeshType && resourceChanged.Operation == events.Delete {
 			r.deleteRateLimiter(resourceChanged.Key.Name)
 		}
@@ -116,7 +123,7 @@ func (r *resyncer) Start(stop <-chan struct{}) error {
 				log.Error(err, "unable to resync ServiceInsight", "mesh", resourceChanged.Key.Mesh)
 			}
 		}
-		if !meshScoped(resourceChanged.Type) {
+		if desc.Scope == model.ScopeGlobal {
 			continue
 		}
 		if resourceChanged.Operation == events.Update && resourceChanged.Type != core_mesh.DataplaneInsightType {
@@ -148,13 +155,6 @@ func (r *resyncer) deleteRateLimiter(mesh string) {
 	delete(r.rateLimiters, mesh)
 }
 
-func meshScoped(t model.ResourceType) bool {
-	if obj, err := registry.Global().NewObject(t); err != nil || obj.Scope() != model.ScopeMesh {
-		return false
-	}
-	return true
-}
-
 func (r *resyncer) createOrUpdateServiceInsights() error {
 	meshes := &core_mesh.MeshResourceList{}
 	if err := r.rm.List(context.Background(), meshes); err != nil {
@@ -330,20 +330,15 @@ func (r *resyncer) createOrUpdateMeshInsight(mesh string) error {
 		updateTotal(envoyVersion, insight.DpVersions.Envoy)
 	}
 
-	for _, resType := range registry.Global().ListTypes() {
-		if !meshScoped(resType) || resType == core_mesh.DataplaneType || resType == core_mesh.DataplaneInsightType {
-			continue
-		}
-		list, err := registry.Global().NewList(resType)
-		if err != nil {
-			return err
-		}
+	for _, resDesc := range r.registry.ObjectDescriptors(model.HasScope(model.ScopeMesh), model.Not(model.Named(core_mesh.DataplaneType, core_mesh.DataplaneInsightType))) {
+		list := resDesc.NewList()
+
 		if err := r.rm.List(context.Background(), list, store.ListByMesh(mesh)); err != nil {
 			return err
 		}
 
 		if len(list.GetItems()) != 0 {
-			insight.Policies[string(resType)] = &mesh_proto.MeshInsight_PolicyStat{
+			insight.Policies[string(resDesc.Name)] = &mesh_proto.MeshInsight_PolicyStat{
 				Total: uint32(len(list.GetItems())),
 			}
 		}
diff --git a/pkg/kds/cache/snapshot.go b/pkg/kds/cache/snapshot.go
index 7a91459fa..09b04ace5 100644
--- a/pkg/kds/cache/snapshot.go
+++ b/pkg/kds/cache/snapshot.go
@@ -8,7 +8,8 @@ import (
 	"github.com/pkg/errors"
 
 	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
-	"github.com/kumahq/kuma/pkg/kds/definitions"
+	"github.com/kumahq/kuma/pkg/core/resources/model"
+	"github.com/kumahq/kuma/pkg/core/resources/registry"
 	util_xds "github.com/kumahq/kuma/pkg/util/xds"
 )
 
@@ -59,8 +60,8 @@ type Snapshot struct {
 var _ util_xds.Snapshot = &Snapshot{}
 
 func (s *Snapshot) GetSupportedTypes() (types []string) {
-	for _, def := range definitions.All {
-		types = append(types, string(def.Type))
+	for _, def := range registry.Global().ObjectTypes(model.HasKdsEnabled()) {
+		types = append(types, string(def))
 	}
 	return
 }
diff --git a/pkg/kds/context/context.go b/pkg/kds/context/context.go
index 9bab1aaf9..078ea0175 100644
--- a/pkg/kds/context/context.go
+++ b/pkg/kds/context/context.go
@@ -43,16 +43,17 @@ func DefaultContext(manager manager.ResourceManager, zone string) *Context {
 // excludes Dataplanes and Ingresses from 'clusterID' cluster
 func GlobalProvidedFilter(rm manager.ResourceManager, configs map[string]bool) reconcile.ResourceFilter {
 	return func(clusterID string, r model.Resource) bool {
-		if r.GetType() == mesh.ZoneIngressType {
+		resType := r.Descriptor().Name
+		if resType == mesh.ZoneIngressType {
 			return r.(*mesh.ZoneIngressResource).Spec.GetZone() != clusterID
 		}
-		if r.GetType() == system.ConfigType && !configs[r.GetMeta().GetName()] {
+		if resType == system.ConfigType && !configs[r.GetMeta().GetName()] {
 			return false
 		}
-		if r.GetType() == system.GlobalSecretType {
+		if resType == system.GlobalSecretType {
 			return zoneingress.IsSigningKeyResource(model.MetaToResourceKey(r.GetMeta()))
 		}
-		if r.GetType() != mesh.DataplaneType {
+		if resType != mesh.DataplaneType {
 			return true
 		}
 		if !r.(*mesh.DataplaneResource).Spec.IsIngress() {
@@ -76,16 +77,17 @@ func GlobalProvidedFilter(rm manager.ResourceManager, configs map[string]bool) r
 // ZoneProvidedFilter filter Resources provided by Zone, specifically Ingresses that belongs to another zones
 func ZoneProvidedFilter(clusterName string) reconcile.ResourceFilter {
 	return func(_ string, r model.Resource) bool {
-		if r.GetType() == mesh.DataplaneType {
+		resType := r.Descriptor().Name
+		if resType == mesh.DataplaneType {
 			return clusterName == util.ZoneTag(r)
 		}
-		if r.GetType() == mesh.DataplaneInsightType {
+		if resType == mesh.DataplaneInsightType {
 			return true
 		}
-		if r.GetType() == mesh.ZoneIngressType && !r.(*mesh.ZoneIngressResource).IsRemoteIngress(clusterName) {
+		if resType == mesh.ZoneIngressType && !r.(*mesh.ZoneIngressResource).IsRemoteIngress(clusterName) {
 			return true
 		}
-		if r.GetType() == mesh.ZoneIngressInsightType {
+		if resType == mesh.ZoneIngressInsightType {
 			return true
 		}
 		return false
diff --git a/pkg/kds/definitions/definitions.go b/pkg/kds/definitions/definitions.go
deleted file mode 100644
index 8616fdb0c..000000000
--- a/pkg/kds/definitions/definitions.go
+++ /dev/null
@@ -1,68 +0,0 @@
-// Package kds provides support of Kuma Discovery Service, extension of xDS
-package definitions
-
-import (
-	"github.com/kumahq/kuma/pkg/core/resources/apis/system"
-	"github.com/kumahq/kuma/pkg/core/resources/model"
-)
-
-const (
-	googleApis = "type.googleapis.com/"
-
-	// KumaResource is the type URL of the KumaResource protobuf.
-	KumaResource = googleApis + "kuma.mesh.v1alpha1.KumaResource"
-)
-
-type KdsDefinition struct {
-	Type      model.ResourceType
-	Direction KDSFlagType
-}
-
-type KDSFlagType uint32
-
-const (
-	ConsumedByZone   = KDSFlagType(1)
-	ConsumedByGlobal = KDSFlagType(1 << 2)
-	ProvidedByZone   = KDSFlagType(1 << 3)
-	ProvidedByGlobal = KDSFlagType(1 << 4)
-	FromZoneToGlobal = ConsumedByGlobal | ProvidedByZone
-	FromGlobalToZone = ProvidedByGlobal | ConsumedByZone
-)
-
-type kdsTypes []KdsDefinition
-
-// Get return a list of all model.ResourceType
-func (kt kdsTypes) Get() []model.ResourceType {
-	var res []model.ResourceType
-	for i := range kt {
-		res = append(res, kt[i].Type)
-	}
-	return res
-}
-
-// Select return a list of all model.ResourceType that have this flag
-func (kt kdsTypes) Select(flag KDSFlagType) []model.ResourceType {
-	var res []model.ResourceType
-	for i := range kt {
-		if kt[i].Direction&flag != 0 {
-			res = append(res, kt[i].Type)
-		}
-	}
-	return res
-}
-
-func (kt kdsTypes) TypeHasFlag(resourceType model.ResourceType, flag KDSFlagType) bool {
-	for _, consumedTyp := range kt {
-		if consumedTyp.Type == resourceType {
-			return 0 != (consumedTyp.Direction & flag)
-		}
-	}
-	return false
-}
-
-var All kdsTypes = append(append([]KdsDefinition{
-	{
-		Type:      system.GlobalSecretType,
-		Direction: FromGlobalToZone,
-	},
-}, meshDefinitions...), systemDefinitions...)
diff --git a/pkg/kds/definitions/generated_mesh_definitions.go b/pkg/kds/definitions/generated_mesh_definitions.go
deleted file mode 100644
index f0dd8b046..000000000
--- a/pkg/kds/definitions/generated_mesh_definitions.go
+++ /dev/null
@@ -1,97 +0,0 @@
-// Generated by tools/resource-gen.
-// Run "make generate" to update this file.
-
-// nolint:whitespace
-package definitions
-
-import (
-	"github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
-)
-
-var meshDefinitions = []KdsDefinition{
-
-	{
-		Type:      mesh.CircuitBreakerType,
-		Direction: FromGlobalToZone,
-	},
-
-	{
-		Type:      mesh.DataplaneType,
-		Direction: FromZoneToGlobal | FromGlobalToZone,
-	},
-
-	{
-		Type:      mesh.DataplaneInsightType,
-		Direction: FromZoneToGlobal,
-	},
-
-	{
-		Type:      mesh.ExternalServiceType,
-		Direction: FromGlobalToZone,
-	},
-
-	{
-		Type:      mesh.FaultInjectionType,
-		Direction: FromGlobalToZone,
-	},
-
-	{
-		Type:      mesh.HealthCheckType,
-		Direction: FromGlobalToZone,
-	},
-
-	{
-		Type:      mesh.MeshType,
-		Direction: FromGlobalToZone,
-	},
-
-	{
-		Type:      mesh.ProxyTemplateType,
-		Direction: FromGlobalToZone,
-	},
-
-	{
-		Type:      mesh.RateLimitType,
-		Direction: FromGlobalToZone,
-	},
-
-	{
-		Type:      mesh.RetryType,
-		Direction: FromGlobalToZone,
-	},
-
-	{
-		Type:      mesh.TimeoutType,
-		Direction: FromGlobalToZone,
-	},
-
-	{
-		Type:      mesh.TrafficLogType,
-		Direction: FromGlobalToZone,
-	},
-
-	{
-		Type:      mesh.TrafficPermissionType,
-		Direction: FromGlobalToZone,
-	},
-
-	{
-		Type:      mesh.TrafficRouteType,
-		Direction: FromGlobalToZone,
-	},
-
-	{
-		Type:      mesh.TrafficTraceType,
-		Direction: FromGlobalToZone,
-	},
-
-	{
-		Type:      mesh.ZoneIngressType,
-		Direction: FromZoneToGlobal | FromGlobalToZone,
-	},
-
-	{
-		Type:      mesh.ZoneIngressInsightType,
-		Direction: FromZoneToGlobal,
-	},
-}
diff --git a/pkg/kds/definitions/generated_system_definitions.go b/pkg/kds/definitions/generated_system_definitions.go
deleted file mode 100644
index 72e88d8de..000000000
--- a/pkg/kds/definitions/generated_system_definitions.go
+++ /dev/null
@@ -1,22 +0,0 @@
-// Generated by tools/resource-gen.
-// Run "make generate" to update this file.
-
-// nolint:whitespace
-package definitions
-
-import (
-	"github.com/kumahq/kuma/pkg/core/resources/apis/system"
-)
-
-var systemDefinitions = []KdsDefinition{
-
-	{
-		Type:      system.ConfigType,
-		Direction: FromGlobalToZone,
-	},
-
-	{
-		Type:      system.SecretType,
-		Direction: FromGlobalToZone,
-	},
-}
diff --git a/pkg/kds/global/components.go b/pkg/kds/global/components.go
index 828a83705..d9c591c07 100644
--- a/pkg/kds/global/components.go
+++ b/pkg/kds/global/components.go
@@ -15,10 +15,10 @@ import (
 	"github.com/kumahq/kuma/pkg/core/resources/apis/system"
 	"github.com/kumahq/kuma/pkg/core/resources/manager"
 	"github.com/kumahq/kuma/pkg/core/resources/model"
+	"github.com/kumahq/kuma/pkg/core/resources/registry"
 	"github.com/kumahq/kuma/pkg/core/resources/store"
 	"github.com/kumahq/kuma/pkg/core/runtime"
 	"github.com/kumahq/kuma/pkg/kds/client"
-	"github.com/kumahq/kuma/pkg/kds/definitions"
 	"github.com/kumahq/kuma/pkg/kds/mux"
 	kds_server "github.com/kumahq/kuma/pkg/kds/server"
 	sync_store "github.com/kumahq/kuma/pkg/kds/store"
@@ -32,7 +32,8 @@ var (
 )
 
 func Setup(rt runtime.Runtime) (err error) {
-	kdsServer, err := kds_server.New(kdsGlobalLog, rt, definitions.All.Select(definitions.ProvidedByGlobal),
+	reg := registry.Global()
+	kdsServer, err := kds_server.New(kdsGlobalLog, rt, reg.ObjectTypes(model.HasKDSFlag(model.ProvidedByGlobal)),
 		"global", rt.Config().Multizone.Global.KDS.RefreshInterval,
 		rt.KDSContext().GlobalProvidedFilter, true)
 	if err != nil {
@@ -53,7 +54,7 @@ func Setup(rt runtime.Runtime) (err error) {
 			log.Error(err, "Global CP could not create a zone")
 			return errors.New("Global CP could not create a zone") // send back message without details. Zone CP will retry
 		}
-		sink := client.NewKDSSink(log, definitions.All.Select(definitions.ConsumedByGlobal), kdsStream, Callbacks(resourceSyncer, rt.Config().Store.Type == store_config.KubernetesStore, kubeFactory))
+		sink := client.NewKDSSink(log, reg.ObjectTypes(model.HasKDSFlag(model.ConsumedByGlobal)), kdsStream, Callbacks(resourceSyncer, rt.Config().Store.Type == store_config.KubernetesStore, kubeFactory))
 		go func() {
 			if err := sink.Start(session.Done()); err != nil {
 				log.Error(err, "KDSSink finished with an error")
diff --git a/pkg/kds/server/type_adjust_callbacks.go b/pkg/kds/server/type_adjust_callbacks.go
index 907b5ebfc..7e371729b 100644
--- a/pkg/kds/server/type_adjust_callbacks.go
+++ b/pkg/kds/server/type_adjust_callbacks.go
@@ -3,7 +3,7 @@ package server
 import (
 	envoy_api "github.com/envoyproxy/go-control-plane/envoy/api/v2"
 
-	"github.com/kumahq/kuma/pkg/kds/definitions"
+	"github.com/kumahq/kuma/pkg/kds"
 	util_xds_v2 "github.com/kumahq/kuma/pkg/util/xds/v2"
 )
 
@@ -21,6 +21,6 @@ type typeAdjustCallbacks struct {
 
 func (c *typeAdjustCallbacks) OnStreamResponse(streamID int64, req *envoy_api.DiscoveryRequest, resp *envoy_api.DiscoveryResponse) {
 	for _, res := range resp.Resources {
-		res.TypeUrl = definitions.KumaResource
+		res.TypeUrl = kds.KumaResource
 	}
 }
diff --git a/pkg/kds/types.go b/pkg/kds/types.go
new file mode 100644
index 000000000..ccf3b18b8
--- /dev/null
+++ b/pkg/kds/types.go
@@ -0,0 +1,8 @@
+package kds
+
+const (
+	googleApis = "type.googleapis.com/"
+
+	// KumaResource is the type URL of the KumaResource protobuf.
+	KumaResource = googleApis + "kuma.mesh.v1alpha1.KumaResource"
+)
diff --git a/pkg/kds/zone/components.go b/pkg/kds/zone/components.go
index 0fe5f3231..98c0a7a24 100644
--- a/pkg/kds/zone/components.go
+++ b/pkg/kds/zone/components.go
@@ -8,10 +8,10 @@ import (
 	"github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	"github.com/kumahq/kuma/pkg/core/resources/apis/system"
 	"github.com/kumahq/kuma/pkg/core/resources/model"
+	"github.com/kumahq/kuma/pkg/core/resources/registry"
 	core_runtime "github.com/kumahq/kuma/pkg/core/runtime"
 	"github.com/kumahq/kuma/pkg/core/runtime/component"
 	kds_client "github.com/kumahq/kuma/pkg/kds/client"
-	"github.com/kumahq/kuma/pkg/kds/definitions"
 	"github.com/kumahq/kuma/pkg/kds/mux"
 	kds_server "github.com/kumahq/kuma/pkg/kds/server"
 	sync_store "github.com/kumahq/kuma/pkg/kds/store"
@@ -27,7 +27,8 @@ var (
 
 func Setup(rt core_runtime.Runtime) error {
 	zone := rt.Config().Multizone.Zone.Name
-	kdsServer, err := kds_server.New(kdsZoneLog, rt, definitions.All.Select(definitions.ProvidedByZone),
+	reg := registry.Global()
+	kdsServer, err := kds_server.New(kdsZoneLog, rt, reg.ObjectTypes(model.HasKDSFlag(model.ProvidedByZone)),
 		zone, rt.Config().Multizone.Zone.KDS.RefreshInterval,
 		rt.KDSContext().ZoneProvidedFilter, false)
 	if err != nil {
@@ -43,7 +44,7 @@ func Setup(rt core_runtime.Runtime) error {
 				log.Error(err, "StreamKumaResources finished with an error")
 			}
 		}()
-		sink := kds_client.NewKDSSink(log, definitions.All.Select(definitions.ConsumedByZone), kds_client.NewKDSStream(session.ClientStream(), zone),
+		sink := kds_client.NewKDSSink(log, reg.ObjectTypes(model.HasKDSFlag(model.ConsumedByZone)), kds_client.NewKDSStream(session.ClientStream(), zone),
 			Callbacks(rt, resourceSyncer, rt.Config().Store.Type == store.KubernetesStore, zone, kubeFactory),
 		)
 		go func() {
diff --git a/pkg/metrics/store/counter.go b/pkg/metrics/store/counter.go
index 9671b4103..86fcce77c 100644
--- a/pkg/metrics/store/counter.go
+++ b/pkg/metrics/store/counter.go
@@ -83,18 +83,15 @@ func (s *storeCounter) count() error {
 }
 
 func (s *storeCounter) countGlobalScopedResources(resourceCount map[string]uint32) error {
-	for _, resType := range registry.Global().ListTypes() {
-		if meshScoped(resType) {
+	for _, resDesc := range registry.Global().ObjectDescriptors() {
+		if resDesc.Scope == model.ScopeMesh {
 			continue
 		}
-		list, err := registry.Global().NewList(resType)
-		if err != nil {
-			return err
-		}
+		list := resDesc.NewList()
 		if err := s.resManager.List(context.Background(), list); err != nil {
 			return err
 		}
-		resourceCount[string(resType)] += uint32(len(list.GetItems()))
+		resourceCount[string(resDesc.Name)] += uint32(len(list.GetItems()))
 	}
 	return nil
 }
@@ -112,10 +109,3 @@ func (s *storeCounter) countMeshScopedResources(resourceCount map[string]uint32)
 	}
 	return nil
 }
-
-func meshScoped(t model.ResourceType) bool {
-	if obj, err := registry.Global().NewObject(t); err != nil || obj.Scope() != model.ScopeMesh {
-		return false
-	}
-	return true
-}
diff --git a/pkg/metrics/store/store.go b/pkg/metrics/store/store.go
index ee448ef0e..1d09263ba 100644
--- a/pkg/metrics/store/store.go
+++ b/pkg/metrics/store/store.go
@@ -33,7 +33,7 @@ func NewMeteredStore(delegate store.ResourceStore, metrics core_metrics.Metrics)
 func (m *MeteredStore) Create(ctx context.Context, resource model.Resource, optionsFunc ...store.CreateOptionsFunc) error {
 	start := core.Now()
 	defer func() {
-		m.metric.WithLabelValues("create", string(resource.GetType())).Observe(core.Now().Sub(start).Seconds())
+		m.metric.WithLabelValues("create", string(resource.Descriptor().Name)).Observe(core.Now().Sub(start).Seconds())
 	}()
 	return m.delegate.Create(ctx, resource, optionsFunc...)
 }
@@ -41,7 +41,7 @@ func (m *MeteredStore) Create(ctx context.Context, resource model.Resource, opti
 func (m *MeteredStore) Update(ctx context.Context, resource model.Resource, optionsFunc ...store.UpdateOptionsFunc) error {
 	start := core.Now()
 	defer func() {
-		m.metric.WithLabelValues("update", string(resource.GetType())).Observe(core.Now().Sub(start).Seconds())
+		m.metric.WithLabelValues("update", string(resource.Descriptor().Name)).Observe(core.Now().Sub(start).Seconds())
 	}()
 	return m.delegate.Update(ctx, resource, optionsFunc...)
 }
@@ -49,7 +49,7 @@ func (m *MeteredStore) Update(ctx context.Context, resource model.Resource, opti
 func (m *MeteredStore) Delete(ctx context.Context, resource model.Resource, optionsFunc ...store.DeleteOptionsFunc) error {
 	start := core.Now()
 	defer func() {
-		m.metric.WithLabelValues("delete", string(resource.GetType())).Observe(core.Now().Sub(start).Seconds())
+		m.metric.WithLabelValues("delete", string(resource.Descriptor().Name)).Observe(core.Now().Sub(start).Seconds())
 	}()
 	return m.delegate.Delete(ctx, resource, optionsFunc...)
 }
@@ -57,7 +57,7 @@ func (m *MeteredStore) Delete(ctx context.Context, resource model.Resource, opti
 func (m *MeteredStore) Get(ctx context.Context, resource model.Resource, optionsFunc ...store.GetOptionsFunc) error {
 	start := core.Now()
 	defer func() {
-		m.metric.WithLabelValues("get", string(resource.GetType())).Observe(core.Now().Sub(start).Seconds())
+		m.metric.WithLabelValues("get", string(resource.Descriptor().Name)).Observe(core.Now().Sub(start).Seconds())
 	}()
 	return m.delegate.Get(ctx, resource, optionsFunc...)
 }
diff --git a/pkg/plugins/config/k8s/store.go b/pkg/plugins/config/k8s/store.go
index 6d29116fe..97584a2a6 100644
--- a/pkg/plugins/config/k8s/store.go
+++ b/pkg/plugins/config/k8s/store.go
@@ -95,7 +95,7 @@ func (s *KubernetesStore) Update(ctx context.Context, r core_model.Resource, fs
 	}
 	if err := s.client.Update(context.Background(), cm); err != nil {
 		if kube_apierrs.IsConflict(err) {
-			return core_store.ErrorResourceConflict(r.GetType(), r.GetMeta().GetName(), r.GetMeta().GetMesh())
+			return core_store.ErrorResourceConflict(r.Descriptor().Name, r.GetMeta().GetName(), r.GetMeta().GetMesh())
 		}
 		return errors.Wrap(err, "failed to update k8s resource")
 	}
@@ -133,7 +133,7 @@ func (s *KubernetesStore) Get(ctx context.Context, r core_model.Resource, fs ...
 	cm := &kube_core.ConfigMap{}
 	if err := s.client.Get(ctx, kube_client.ObjectKey{Namespace: s.namespace, Name: opts.Name}, cm); err != nil {
 		if kube_apierrs.IsNotFound(err) {
-			return core_store.ErrorResourceNotFound(r.GetType(), opts.Name, opts.Mesh)
+			return core_store.ErrorResourceNotFound(r.Descriptor().Name, opts.Name, opts.Mesh)
 		}
 		return errors.Wrap(err, "failed to get k8s Config")
 	}
diff --git a/pkg/plugins/resources/k8s/store.go b/pkg/plugins/resources/k8s/store.go
index 9ecdb46a8..972f18ea6 100644
--- a/pkg/plugins/resources/k8s/store.go
+++ b/pkg/plugins/resources/k8s/store.go
@@ -66,7 +66,7 @@ func (s *KubernetesStore) Create(ctx context.Context, r core_model.Resource, fs
 
 	if err := s.Client.Create(ctx, obj); err != nil {
 		if kube_apierrs.IsAlreadyExists(err) {
-			return store.ErrorResourceAlreadyExists(r.GetType(), opts.Name, opts.Mesh)
+			return store.ErrorResourceAlreadyExists(r.Descriptor().Name, opts.Name, opts.Mesh)
 		}
 		return errors.Wrap(err, "failed to create k8s resource")
 	}
@@ -91,7 +91,7 @@ func (s *KubernetesStore) Update(ctx context.Context, r core_model.Resource, fs
 
 	obj, err := s.Converter.ToKubernetesObject(r)
 	if err != nil {
-		return errors.Wrapf(err, "failed to convert core model of type %s into k8s counterpart", r.GetType())
+		return errors.Wrapf(err, "failed to convert core model of type %s into k8s counterpart", r.Descriptor().Name)
 	}
 
 	if opts.Synced {
@@ -100,7 +100,7 @@ func (s *KubernetesStore) Update(ctx context.Context, r core_model.Resource, fs
 
 	if err := s.Client.Update(ctx, obj); err != nil {
 		if kube_apierrs.IsConflict(err) {
-			return store.ErrorResourceConflict(r.GetType(), r.GetMeta().GetName(), r.GetMeta().GetMesh())
+			return store.ErrorResourceConflict(r.Descriptor().Name, r.GetMeta().GetName(), r.GetMeta().GetMesh())
 		}
 		return errors.Wrap(err, "failed to update k8s resource")
 	}
@@ -121,7 +121,7 @@ func (s *KubernetesStore) Delete(ctx context.Context, r core_model.Resource, fs
 
 	obj, err := s.Converter.ToKubernetesObject(r)
 	if err != nil {
-		return errors.Wrapf(err, "failed to convert core model of type %s into k8s counterpart", r.GetType())
+		return errors.Wrapf(err, "failed to convert core model of type %s into k8s counterpart", r.Descriptor().Name)
 	}
 	name, namespace, err := k8sNameNamespace(opts.Name, obj.Scope())
 	if err != nil {
@@ -142,7 +142,7 @@ func (s *KubernetesStore) Get(ctx context.Context, r core_model.Resource, fs ...
 	opts := store.NewGetOptions(fs...)
 	obj, err := s.Converter.ToKubernetesObject(r)
 	if err != nil {
-		return errors.Wrapf(err, "failed to convert core model of type %s into k8s counterpart", r.GetType())
+		return errors.Wrapf(err, "failed to convert core model of type %s into k8s counterpart", r.Descriptor().Name)
 	}
 	name, namespace, err := k8sNameNamespace(opts.Name, obj.Scope())
 	if err != nil {
@@ -150,7 +150,7 @@ func (s *KubernetesStore) Get(ctx context.Context, r core_model.Resource, fs ...
 	}
 	if err := s.Client.Get(ctx, kube_client.ObjectKey{Namespace: namespace, Name: name}, obj); err != nil {
 		if kube_apierrs.IsNotFound(err) {
-			return store.ErrorResourceNotFound(r.GetType(), opts.Name, opts.Mesh)
+			return store.ErrorResourceNotFound(r.Descriptor().Name, opts.Name, opts.Mesh)
 		}
 		return errors.Wrap(err, "failed to get k8s resource")
 	}
@@ -158,10 +158,10 @@ func (s *KubernetesStore) Get(ctx context.Context, r core_model.Resource, fs ...
 		return errors.Wrap(err, "failed to convert k8s model into core counterpart")
 	}
 	if opts.Version != "" && r.GetMeta().GetVersion() != opts.Version {
-		return store.ErrorResourcePreconditionFailed(r.GetType(), opts.Name, opts.Mesh)
+		return store.ErrorResourcePreconditionFailed(r.Descriptor().Name, opts.Name, opts.Mesh)
 	}
 	if r.GetMeta().GetMesh() != opts.Mesh {
-		return store.ErrorResourceNotFound(r.GetType(), opts.Name, opts.Mesh)
+		return store.ErrorResourceNotFound(r.Descriptor().Name, opts.Name, opts.Mesh)
 	}
 	return nil
 }
diff --git a/pkg/plugins/resources/memory/store.go b/pkg/plugins/resources/memory/store.go
index c65e374d9..6d826443e 100644
--- a/pkg/plugins/resources/memory/store.go
+++ b/pkg/plugins/resources/memory/store.go
@@ -102,8 +102,8 @@ func (c *memoryStore) Create(_ context.Context, r model.Resource, fs ...store.Cr
 
 	opts := store.NewCreateOptions(fs...)
 	// Name must be provided via CreateOptions
-	if _, record := c.findRecord(string(r.GetType()), opts.Name, opts.Mesh); record != nil {
-		return store.ErrorResourceAlreadyExists(r.GetType(), opts.Name, opts.Mesh)
+	if _, record := c.findRecord(string(r.Descriptor().Name), opts.Name, opts.Mesh); record != nil {
+		return store.ErrorResourceAlreadyExists(r.Descriptor().Name, opts.Name, opts.Mesh)
 	}
 
 	meta := memoryMeta{
@@ -119,7 +119,7 @@ func (c *memoryStore) Create(_ context.Context, r model.Resource, fs ...store.Cr
 
 	// convert into storage representation
 	record, err := c.marshalRecord(
-		string(r.GetType()),
+		string(r.Descriptor().Name),
 		meta,
 		r.GetSpec())
 	if err != nil {
@@ -127,9 +127,9 @@ func (c *memoryStore) Create(_ context.Context, r model.Resource, fs ...store.Cr
 	}
 
 	if opts.Owner != nil {
-		_, ownerRecord := c.findRecord(string(opts.Owner.GetType()), opts.Owner.GetMeta().GetName(), opts.Owner.GetMeta().GetMesh())
+		_, ownerRecord := c.findRecord(string(opts.Owner.Descriptor().Name), opts.Owner.GetMeta().GetName(), opts.Owner.GetMeta().GetMesh())
 		if ownerRecord == nil {
-			return store.ErrorResourceNotFound(opts.Owner.GetType(), opts.Owner.GetMeta().GetName(), opts.Owner.GetMeta().GetMesh())
+			return store.ErrorResourceNotFound(opts.Owner.Descriptor().Name, opts.Owner.GetMeta().GetName(), opts.Owner.GetMeta().GetMesh())
 		}
 		ownerRecord.Children = append(ownerRecord.Children, &record.resourceKey)
 	}
@@ -140,7 +140,7 @@ func (c *memoryStore) Create(_ context.Context, r model.Resource, fs ...store.Cr
 		go func() {
 			c.eventWriter.Send(events.ResourceChangedEvent{
 				Operation: events.Create,
-				Type:      r.GetType(),
+				Type:      r.Descriptor().Name,
 				Key:       model.MetaToResourceKey(r.GetMeta()),
 			})
 		}()
@@ -161,15 +161,15 @@ func (c *memoryStore) Update(_ context.Context, r model.Resource, fs ...store.Up
 
 	// Name must be provided via r.GetMeta()
 	mesh := r.GetMeta().GetMesh()
-	idx, record := c.findRecord(string(r.GetType()), r.GetMeta().GetName(), mesh)
+	idx, record := c.findRecord(string(r.Descriptor().Name), r.GetMeta().GetName(), mesh)
 	if record == nil || meta.Version != record.Version {
-		return store.ErrorResourceConflict(r.GetType(), r.GetMeta().GetName(), r.GetMeta().GetMesh())
+		return store.ErrorResourceConflict(r.Descriptor().Name, r.GetMeta().GetName(), r.GetMeta().GetMesh())
 	}
 	meta.Version = meta.Version.Next()
 	meta.ModificationTime = opts.ModificationTime
 
 	record, err := c.marshalRecord(
-		string(r.GetType()),
+		string(r.Descriptor().Name),
 		meta,
 		r.GetSpec())
 	if err != nil {
@@ -184,7 +184,7 @@ func (c *memoryStore) Update(_ context.Context, r model.Resource, fs ...store.Up
 		go func() {
 			c.eventWriter.Send(events.ResourceChangedEvent{
 				Operation: events.Update,
-				Type:      r.GetType(),
+				Type:      r.Descriptor().Name,
 				Key:       model.MetaToResourceKey(r.GetMeta()),
 			})
 		}()
@@ -207,9 +207,9 @@ func (c *memoryStore) delete(ctx context.Context, r model.Resource, fs ...store.
 	}
 
 	// Name must be provided via DeleteOptions
-	idx, record := c.findRecord(string(r.GetType()), opts.Name, opts.Mesh)
+	idx, record := c.findRecord(string(r.Descriptor().Name), opts.Name, opts.Mesh)
 	if record == nil {
-		return store.ErrorResourceNotFound(r.GetType(), opts.Name, opts.Mesh)
+		return store.ErrorResourceNotFound(r.Descriptor().Name, opts.Name, opts.Mesh)
 	}
 	for _, child := range record.Children {
 		_, childRecord := c.findRecord(child.ResourceType, child.Name, child.Mesh)
@@ -232,7 +232,7 @@ func (c *memoryStore) delete(ctx context.Context, r model.Resource, fs ...store.
 		go func() {
 			c.eventWriter.Send(events.ResourceChangedEvent{
 				Operation: events.Delete,
-				Type:      r.GetType(),
+				Type:      r.Descriptor().Name,
 				Key: model.ResourceKey{
 					Mesh: opts.Mesh,
 					Name: opts.Name,
@@ -249,12 +249,12 @@ func (c *memoryStore) Get(_ context.Context, r model.Resource, fs ...store.GetOp
 
 	opts := store.NewGetOptions(fs...)
 	// Name must be provided via GetOptions
-	_, record := c.findRecord(string(r.GetType()), opts.Name, opts.Mesh)
+	_, record := c.findRecord(string(r.Descriptor().Name), opts.Name, opts.Mesh)
 	if record == nil {
-		return store.ErrorResourceNotFound(r.GetType(), opts.Name, opts.Mesh)
+		return store.ErrorResourceNotFound(r.Descriptor().Name, opts.Name, opts.Mesh)
 	}
 	if opts.Version != "" && opts.Version != record.Version.String() {
-		return store.ErrorResourcePreconditionFailed(r.GetType(), opts.Name, opts.Mesh)
+		return store.ErrorResourcePreconditionFailed(r.Descriptor().Name, opts.Name, opts.Mesh)
 	}
 	return c.unmarshalRecord(record, r)
 }
diff --git a/pkg/plugins/resources/postgres/store.go b/pkg/plugins/resources/postgres/store.go
index e02e4ac1f..c87292ec2 100644
--- a/pkg/plugins/resources/postgres/store.go
+++ b/pkg/plugins/resources/postgres/store.go
@@ -59,16 +59,16 @@ func (r *postgresResourceStore) Create(_ context.Context, resource model.Resourc
 		ptr := func(s string) *string { return &s }
 		ownerName = ptr(opts.Owner.GetMeta().GetName())
 		ownerMesh = ptr(opts.Owner.GetMeta().GetMesh())
-		ownerType = ptr(string(opts.Owner.GetType()))
+		ownerType = ptr(string(opts.Owner.Descriptor().Name))
 	}
 
 	version := 0
 	statement := `INSERT INTO resources VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);`
-	_, err = r.db.Exec(statement, opts.Name, opts.Mesh, resource.GetType(), version, string(bytes),
+	_, err = r.db.Exec(statement, opts.Name, opts.Mesh, resource.Descriptor().Name, version, string(bytes),
 		opts.CreationTime.UTC(), opts.CreationTime.UTC(), ownerName, ownerMesh, ownerType)
 	if err != nil {
 		if strings.Contains(err.Error(), duplicateKeyErrorMsg) {
-			return store.ErrorResourceAlreadyExists(resource.GetType(), opts.Name, opts.Mesh)
+			return store.ErrorResourceAlreadyExists(resource.Descriptor().Name, opts.Name, opts.Mesh)
 		}
 		return errors.Wrapf(err, "failed to execute query: %s", statement)
 	}
@@ -104,14 +104,14 @@ func (r *postgresResourceStore) Update(_ context.Context, resource model.Resourc
 		opts.ModificationTime.UTC(),
 		resource.GetMeta().GetName(),
 		resource.GetMeta().GetMesh(),
-		resource.GetType(),
+		resource.Descriptor().Name,
 		version,
 	)
 	if err != nil {
 		return errors.Wrapf(err, "failed to execute query %s", statement)
 	}
 	if rows, _ := result.RowsAffected(); rows != 1 { // error ignored, postgres supports RowsAffected()
-		return store.ErrorResourceConflict(resource.GetType(), resource.GetMeta().GetName(), resource.GetMeta().GetMesh())
+		return store.ErrorResourceConflict(resource.Descriptor().Name, resource.GetMeta().GetName(), resource.GetMeta().GetMesh())
 	}
 
 	// update resource's meta with new version
@@ -129,12 +129,12 @@ func (r *postgresResourceStore) Delete(_ context.Context, resource model.Resourc
 	opts := store.NewDeleteOptions(fs...)
 
 	statement := `DELETE FROM resources WHERE name=$1 AND type=$2 AND mesh=$3`
-	result, err := r.db.Exec(statement, opts.Name, resource.GetType(), opts.Mesh)
+	result, err := r.db.Exec(statement, opts.Name, resource.Descriptor().Name, opts.Mesh)
 	if err != nil {
 		return errors.Wrapf(err, "failed to execute query: %s", statement)
 	}
 	if rows, _ := result.RowsAffected(); rows == 0 { // error ignored, postgres supports RowsAffected()
-		return store.ErrorResourceNotFound(resource.GetType(), opts.Name, opts.Mesh)
+		return store.ErrorResourceNotFound(resource.Descriptor().Name, opts.Name, opts.Mesh)
 	}
 
 	return nil
@@ -144,14 +144,14 @@ func (r *postgresResourceStore) Get(_ context.Context, resource model.Resource,
 	opts := store.NewGetOptions(fs...)
 
 	statement := `SELECT spec, version, creation_time, modification_time FROM resources WHERE name=$1 AND mesh=$2 AND type=$3;`
-	row := r.db.QueryRow(statement, opts.Name, opts.Mesh, resource.GetType())
+	row := r.db.QueryRow(statement, opts.Name, opts.Mesh, resource.Descriptor().Name)
 
 	var spec string
 	var version int
 	var creationTime, modificationTime time.Time
 	err := row.Scan(&spec, &version, &creationTime, &modificationTime)
 	if err == sql.ErrNoRows {
-		return store.ErrorResourceNotFound(resource.GetType(), opts.Name, opts.Mesh)
+		return store.ErrorResourceNotFound(resource.Descriptor().Name, opts.Name, opts.Mesh)
 	}
 	if err != nil {
 		return errors.Wrapf(err, "failed to execute query: %s", statement)
@@ -171,7 +171,7 @@ func (r *postgresResourceStore) Get(_ context.Context, resource model.Resource,
 	resource.SetMeta(meta)
 
 	if opts.Version != "" && resource.GetMeta().GetVersion() != opts.Version {
-		return store.ErrorResourcePreconditionFailed(resource.GetType(), opts.Name, opts.Mesh)
+		return store.ErrorResourcePreconditionFailed(resource.Descriptor().Name, opts.Name, opts.Mesh)
 	}
 	return nil
 }
diff --git a/pkg/plugins/resources/remote/store.go b/pkg/plugins/resources/remote/store.go
index 633ee6272..591109602 100644
--- a/pkg/plugins/resources/remote/store.go
+++ b/pkg/plugins/resources/remote/store.go
@@ -34,7 +34,7 @@ type remoteStore struct {
 func (s *remoteStore) Create(ctx context.Context, res model.Resource, fs ...store.CreateOptionsFunc) error {
 	opts := store.NewCreateOptions(fs...)
 	meta := rest.ResourceMeta{
-		Type: string(res.GetType()),
+		Type: string(res.Descriptor().Name),
 		Name: opts.Name,
 		Mesh: opts.Mesh,
 	}
@@ -46,7 +46,7 @@ func (s *remoteStore) Create(ctx context.Context, res model.Resource, fs ...stor
 
 func (s *remoteStore) Update(ctx context.Context, res model.Resource, fs ...store.UpdateOptionsFunc) error {
 	meta := rest.ResourceMeta{
-		Type: string(res.GetType()),
+		Type: string(res.Descriptor().Name),
 		Name: res.GetMeta().GetName(),
 		Mesh: res.GetMeta().GetMesh(),
 	}
@@ -57,9 +57,9 @@ func (s *remoteStore) Update(ctx context.Context, res model.Resource, fs ...stor
 }
 
 func (s *remoteStore) upsert(ctx context.Context, res model.Resource, meta rest.ResourceMeta) error {
-	resourceApi, err := s.api.GetResourceApi(res.GetType())
+	resourceApi, err := s.api.GetResourceApi(res.Descriptor().Name)
 	if err != nil {
-		return errors.Wrapf(err, "failed to construct URI to update a %q", res.GetType())
+		return errors.Wrapf(err, "failed to construct URI to update a %q", res.Descriptor().Name)
 	}
 	restRes := rest.Resource{
 		Meta: meta,
@@ -95,9 +95,9 @@ func (s *remoteStore) upsert(ctx context.Context, res model.Resource, meta rest.
 
 func (s *remoteStore) Delete(ctx context.Context, res model.Resource, fs ...store.DeleteOptionsFunc) error {
 	opts := store.NewDeleteOptions(fs...)
-	resourceApi, err := s.api.GetResourceApi(res.GetType())
+	resourceApi, err := s.api.GetResourceApi(res.Descriptor().Name)
 	if err != nil {
-		return errors.Wrapf(err, "failed to construct URI to delete a %q", res.GetType())
+		return errors.Wrapf(err, "failed to construct URI to delete a %q", res.Descriptor().Name)
 	}
 	req, err := http.NewRequest("DELETE", resourceApi.Item(opts.Mesh, opts.Name), nil)
 	if err != nil {
@@ -106,7 +106,7 @@ func (s *remoteStore) Delete(ctx context.Context, res model.Resource, fs ...stor
 	statusCode, b, err := s.doRequest(ctx, req)
 	if err != nil {
 		if statusCode == 404 {
-			return store.ErrorResourceNotFound(res.GetType(), opts.Name, opts.Mesh)
+			return store.ErrorResourceNotFound(res.Descriptor().Name, opts.Name, opts.Mesh)
 		}
 		return err
 	}
@@ -121,9 +121,9 @@ func (s *remoteStore) Delete(ctx context.Context, res model.Resource, fs ...stor
 }
 
 func (s *remoteStore) Get(ctx context.Context, res model.Resource, fs ...store.GetOptionsFunc) error {
-	resourceApi, err := s.api.GetResourceApi(res.GetType())
+	resourceApi, err := s.api.GetResourceApi(res.Descriptor().Name)
 	if err != nil {
-		return errors.Wrapf(err, "failed to construct URI to fetch a %q", res.GetType())
+		return errors.Wrapf(err, "failed to construct URI to fetch a %q", res.Descriptor().Name)
 	}
 	opts := store.NewGetOptions(fs...)
 	req, err := http.NewRequest("GET", resourceApi.Item(opts.Mesh, opts.Name), nil)
@@ -133,7 +133,7 @@ func (s *remoteStore) Get(ctx context.Context, res model.Resource, fs ...store.G
 	statusCode, b, err := s.doRequest(ctx, req)
 	if err != nil {
 		if statusCode == 404 {
-			return store.ErrorResourceNotFound(res.GetType(), opts.Name, opts.Mesh)
+			return store.ErrorResourceNotFound(res.Descriptor().Name, opts.Name, opts.Mesh)
 		}
 		return err
 	}
diff --git a/pkg/plugins/runtime/gateway/enabled.go b/pkg/plugins/runtime/gateway/enabled.go
index 11aae37c0..8605ca8e3 100644
--- a/pkg/plugins/runtime/gateway/enabled.go
+++ b/pkg/plugins/runtime/gateway/enabled.go
@@ -3,10 +3,10 @@
 package gateway
 
 import (
-	core_plugins "github.com/kumahq/kuma/pkg/core/plugins"
+	"github.com/kumahq/kuma/pkg/core/plugins"
 	_ "github.com/kumahq/kuma/pkg/plugins/runtime/gateway/register"
 )
 
 func init() {
-	core_plugins.Register("gateway", &plugin{})
+	plugins.Register("gateway", &plugin{})
 }
diff --git a/pkg/plugins/runtime/gateway/register/enabled.go b/pkg/plugins/runtime/gateway/register/enabled.go
index a92604ef3..5c7e203f9 100644
--- a/pkg/plugins/runtime/gateway/register/enabled.go
+++ b/pkg/plugins/runtime/gateway/register/enabled.go
@@ -3,10 +3,8 @@
 package register
 
 import (
-	"github.com/kumahq/kuma/app/kumactl/pkg/entities"
 	core_mesh "github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	"github.com/kumahq/kuma/pkg/core/resources/registry"
-	kds_definitions "github.com/kumahq/kuma/pkg/kds/definitions"
 )
 
 // NOTE: this is non-deterministic in testing. Some tests will import
@@ -14,9 +12,6 @@ import (
 // whether the Gateway types are registered in tests depends on which
 // subset of tests are running.
 func init() {
-	registry.RegisterType(core_mesh.NewGatewayResource())
-	registry.RegistryListType(&core_mesh.GatewayResourceList{})
-
 	// A Gateway is local to a zone, which means that it propagates in one
 	// direction, from a zone CP up to a global CP. The reason for this
 	// is that the Kubernetes Gateway API is the native Kubernetes API
@@ -24,16 +19,5 @@ func init() {
 	// to a Kubernetes zone, we would need to be able to transform Gateway
 	// resources from Universal -> Kubernetes and have to deal with namespace
 	// semantics and a lot of other unpleasantness.
-
-	kds_definitions.All = append(kds_definitions.All,
-		kds_definitions.KdsDefinition{
-			Type:      core_mesh.GatewayType,
-			Direction: kds_definitions.FromZoneToGlobal,
-		})
-
-	entities.All = append(entities.All, entities.Definition{
-		Singular:     "gateway",
-		Plural:       "gateways",
-		ResourceType: core_mesh.GatewayType,
-	})
+	registry.RegisterType(core_mesh.GatewayResourceTypeDescriptor)
 }
diff --git a/pkg/plugins/runtime/k8s/webhooks/owner_reference_mutator.go b/pkg/plugins/runtime/k8s/webhooks/owner_reference_mutator.go
index 565f799b0..ed60a83aa 100644
--- a/pkg/plugins/runtime/k8s/webhooks/owner_reference_mutator.go
+++ b/pkg/plugins/runtime/k8s/webhooks/owner_reference_mutator.go
@@ -57,7 +57,7 @@ func (m *OwnerReferenceMutator) Handle(ctx context.Context, req admission.Reques
 		}
 	default:
 		// we need to also validate Mesh here because OwnerReferenceMutator is executed before validatingHandler
-		if err := core_mesh.ValidateMesh(obj.GetMesh(), coreRes.Scope()); err.HasViolations() {
+		if err := core_mesh.ValidateMesh(obj.GetMesh(), coreRes.Descriptor().Scope); err.HasViolations() {
 			return convertValidationErrorOf(err, obj, obj.GetObjectMeta())
 		}
 
diff --git a/pkg/plugins/runtime/k8s/webhooks/validation.go b/pkg/plugins/runtime/k8s/webhooks/validation.go
index f842b0af9..46d24d48d 100644
--- a/pkg/plugins/runtime/k8s/webhooks/validation.go
+++ b/pkg/plugins/runtime/k8s/webhooks/validation.go
@@ -18,7 +18,6 @@ import (
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	core_registry "github.com/kumahq/kuma/pkg/core/resources/registry"
 	"github.com/kumahq/kuma/pkg/core/validators"
-	"github.com/kumahq/kuma/pkg/kds/definitions"
 	k8s_common "github.com/kumahq/kuma/pkg/plugins/common/k8s"
 	k8s_model "github.com/kumahq/kuma/pkg/plugins/resources/k8s/native/pkg/model"
 	k8s_registry "github.com/kumahq/kuma/pkg/plugins/resources/k8s/native/pkg/registry"
@@ -78,7 +77,7 @@ func (h *validatingHandler) Handle(ctx context.Context, req admission.Request) a
 		return admission.Errored(http.StatusInternalServerError, err)
 	}
 
-	if err := core_mesh.ValidateMesh(obj.GetMesh(), coreRes.Scope()); err.HasViolations() {
+	if err := core_mesh.ValidateMesh(obj.GetMesh(), coreRes.Descriptor().Scope); err.HasViolations() {
 		return convertValidationErrorOf(err, obj, obj.GetObjectMeta())
 	}
 
@@ -104,16 +103,12 @@ func (h *validatingHandler) validateSync(resType core_model.ResourceType, obj k8
 		return admission.Allowed("")
 	}
 
-	switch h.mode {
-	case core.Zone:
-		// Although Remote CP consumes Dataplane (Ingress) we also apply Dataplane on Remote
-		if resType != core_mesh.DataplaneType && definitions.All.TypeHasFlag(resType, definitions.ConsumedByZone) {
-			return syncErrorResponse(resType, core.Zone)
-		}
-	case core.Global:
-		if definitions.All.TypeHasFlag(resType, definitions.ConsumedByGlobal) {
-			return syncErrorResponse(resType, core.Global)
-		}
+	descriptor, err := h.coreRegistry.DescriptorFor(resType)
+	if err != nil {
+		return syncErrorResponse(resType, h.mode)
+	}
+	if (h.mode == core.Global && descriptor.KDSFlags.Has(core_model.ConsumedByGlobal)) || (h.mode == core.Zone && resType != core_mesh.DataplaneType && descriptor.KDSFlags.Has(core_model.ConsumedByZone)) {
+		return syncErrorResponse(resType, h.mode)
 	}
 	return admission.Allowed("")
 }
diff --git a/pkg/plugins/secrets/k8s/store.go b/pkg/plugins/secrets/k8s/store.go
index d5bd0cbc3..b3594216f 100644
--- a/pkg/plugins/secrets/k8s/store.go
+++ b/pkg/plugins/secrets/k8s/store.go
@@ -52,7 +52,7 @@ func (s *KubernetesStore) Create(ctx context.Context, r core_model.Resource, fs
 	}
 	secret.Namespace = s.namespace
 	secret.Name = opts.Name
-	if r.GetType() == secret_model.SecretType {
+	if r.Descriptor().Name == secret_model.SecretType {
 		labels := map[string]string{
 			meshLabel: opts.Mesh,
 		}
@@ -61,7 +61,7 @@ func (s *KubernetesStore) Create(ctx context.Context, r core_model.Resource, fs
 
 	if err := s.writer.Create(ctx, secret); err != nil {
 		if kube_apierrs.IsAlreadyExists(err) {
-			return core_store.ErrorResourceAlreadyExists(r.GetType(), secret.Name, opts.Mesh)
+			return core_store.ErrorResourceAlreadyExists(r.Descriptor().Name, secret.Name, opts.Mesh)
 		}
 		return errors.Wrap(err, "failed to create k8s Secret")
 	}
@@ -79,7 +79,7 @@ func (s *KubernetesStore) Update(ctx context.Context, r core_model.Resource, fs
 	secret.Namespace = s.namespace
 	if err := s.writer.Update(ctx, secret); err != nil {
 		if kube_apierrs.IsConflict(err) {
-			return core_store.ErrorResourceConflict(r.GetType(), secret.Name, r.GetMeta().GetMesh())
+			return core_store.ErrorResourceConflict(r.Descriptor().Name, secret.Name, r.GetMeta().GetMesh())
 		}
 		return errors.Wrap(err, "failed to update k8s Secret")
 	}
@@ -112,7 +112,7 @@ func (s *KubernetesStore) Get(ctx context.Context, r core_model.Resource, fs ...
 	secret := &kube_core.Secret{}
 	if err := s.reader.Get(ctx, kube_client.ObjectKey{Namespace: s.namespace, Name: opts.Name}, secret); err != nil {
 		if kube_apierrs.IsNotFound(err) {
-			return core_store.ErrorResourceNotFound(r.GetType(), opts.Name, opts.Mesh)
+			return core_store.ErrorResourceNotFound(r.Descriptor().Name, opts.Name, opts.Mesh)
 		}
 		return errors.Wrap(err, "failed to get k8s secret")
 	}
@@ -126,14 +126,14 @@ func (s *KubernetesStore) Get(ctx context.Context, r core_model.Resource, fs ...
 }
 
 func assertFound(r core_model.Resource, secret *kube_core.Secret, name string, mesh string) error {
-	switch r.GetType() {
+	switch r.Descriptor().Name {
 	case secret_model.SecretType:
 		// secret must match mesh and be a proper type, otherwise return not found
 		if r.GetMeta().GetMesh() != mesh || secret.Type != common_k8s.MeshSecretType {
 			if err := r.SetSpec(&system_proto.Secret{}); err != nil {
 				return err
 			}
-			return core_store.ErrorResourceNotFound(r.GetType(), name, mesh)
+			return core_store.ErrorResourceNotFound(r.Descriptor().Name, name, mesh)
 		}
 	case secret_model.GlobalSecretType:
 		// secret must be a proper type, otherwise return not found
@@ -141,7 +141,7 @@ func assertFound(r core_model.Resource, secret *kube_core.Secret, name string, m
 			if err := r.SetSpec(&system_proto.Secret{}); err != nil {
 				return err
 			}
-			return core_store.ErrorResourceNotFound(r.GetType(), name, mesh)
+			return core_store.ErrorResourceNotFound(r.Descriptor().Name, name, mesh)
 		}
 	}
 	return nil
@@ -226,7 +226,7 @@ type SimpleConverter struct {
 
 func (c *SimpleConverter) ToKubernetesObject(r core_model.Resource) (*kube_core.Secret, error) {
 	secret := &kube_core.Secret{}
-	switch r.GetType() {
+	switch r.Descriptor().Name {
 	case secret_model.SecretType:
 		secret.Type = common_k8s.MeshSecretType
 		secret.Data = map[string][]byte{
@@ -244,7 +244,7 @@ func (c *SimpleConverter) ToKubernetesObject(r core_model.Resource) (*kube_core.
 			"value": r.(*secret_model.GlobalSecretResource).Spec.GetData().GetValue(),
 		}
 	default:
-		return nil, errors.Errorf("invalid type %s, expected %s or %s", r.GetType(), secret_model.SecretType, secret_model.GlobalSecretType)
+		return nil, errors.Errorf("invalid type %s, expected %s or %s", r.Descriptor().Name, secret_model.SecretType, secret_model.GlobalSecretType)
 	}
 	if r.GetMeta() != nil {
 		if adapter, ok := r.GetMeta().(*KubernetesMetaAdapter); ok {
diff --git a/pkg/xds/auth/callbacks.go b/pkg/xds/auth/callbacks.go
index 0ccc935fc..c65f439de 100644
--- a/pkg/xds/auth/callbacks.go
+++ b/pkg/xds/auth/callbacks.go
@@ -146,7 +146,7 @@ func (a *authCallbacks) authenticate(credential Credential, req util_xds.Discove
 			if core_store.IsResourceNotFound(err) {
 				return retry.RetryableError(errors.Errorf(
 					"resource %q not found; create a %s in Kuma CP first or pass it as an argument to kuma-dp",
-					proxyId, resource.GetType()))
+					proxyId, resource.Descriptor().Name))
 			}
 			return err
 		})
diff --git a/pkg/xds/auth/k8s/authenticator.go b/pkg/xds/auth/k8s/authenticator.go
index 16afc4117..6c2cbe791 100644
--- a/pkg/xds/auth/k8s/authenticator.go
+++ b/pkg/xds/auth/k8s/authenticator.go
@@ -33,7 +33,7 @@ func (k *kubeAuthenticator) Authenticate(ctx context.Context, resource model.Res
 	case *core_mesh.ZoneIngressResource:
 		return k.authZoneIngress(ctx, resource, credential)
 	default:
-		return errors.Errorf("no matching authenticator for %s resource", resource.GetType())
+		return errors.Errorf("no matching authenticator for %s resource", resource.Descriptor().Name)
 	}
 }
 
diff --git a/pkg/xds/auth/universal/authenticator.go b/pkg/xds/auth/universal/authenticator.go
index 7403236f4..b0a71faa4 100644
--- a/pkg/xds/auth/universal/authenticator.go
+++ b/pkg/xds/auth/universal/authenticator.go
@@ -45,7 +45,7 @@ func (u *universalAuthenticator) Authenticate(ctx context.Context, resource mode
 	case *core_mesh.ZoneIngressResource:
 		return u.authZoneIngress(ctx, resource, credential)
 	default:
-		return errors.Errorf("no matching authenticator for %s resource", resource.GetType())
+		return errors.Errorf("no matching authenticator for %s resource", resource.Descriptor().Name)
 	}
 }
 
diff --git a/pkg/xds/auth/universal/noop_authenticator.go b/pkg/xds/auth/universal/noop_authenticator.go
index 83f5f557f..72fb76b1b 100644
--- a/pkg/xds/auth/universal/noop_authenticator.go
+++ b/pkg/xds/auth/universal/noop_authenticator.go
@@ -26,6 +26,6 @@ func (u *noopAuthenticator) Authenticate(ctx context.Context, resource model.Res
 	case *core_mesh.ZoneIngressResource:
 		return nil
 	default:
-		return errors.Errorf("no matching authenticator for %s resource", resource.GetType())
+		return errors.Errorf("no matching authenticator for %s resource", resource.Descriptor().Name)
 	}
 }
diff --git a/pkg/xds/cache/mesh/snapshot.go b/pkg/xds/cache/mesh/snapshot.go
index 4874eab19..d81a47a5f 100644
--- a/pkg/xds/cache/mesh/snapshot.go
+++ b/pkg/xds/cache/mesh/snapshot.go
@@ -120,7 +120,7 @@ func (m *meshSnapshot) hashResource(r core_model.Resource) string {
 	// after resolving. That's why it is important to include address into hash.
 	case *core_mesh.DataplaneResource:
 		return strings.Join(
-			[]string{string(v.GetType()),
+			[]string{string(v.Descriptor().Name),
 				v.GetMeta().GetMesh(),
 				v.GetMeta().GetName(),
 				v.GetMeta().GetVersion(),
@@ -129,7 +129,7 @@ func (m *meshSnapshot) hashResource(r core_model.Resource) string {
 			}, ":")
 	case *core_mesh.ZoneIngressResource:
 		return strings.Join(
-			[]string{string(v.GetType()),
+			[]string{string(v.Descriptor().Name),
 				v.GetMeta().GetMesh(),
 				v.GetMeta().GetName(),
 				v.GetMeta().GetVersion(),
@@ -138,7 +138,7 @@ func (m *meshSnapshot) hashResource(r core_model.Resource) string {
 			}, ":")
 	default:
 		return strings.Join(
-			[]string{string(v.GetType()),
+			[]string{string(v.Descriptor().Name),
 				v.GetMeta().GetMesh(),
 				v.GetMeta().GetName(),
 				v.GetMeta().GetVersion()}, ":")
diff --git a/pkg/xds/server/components.go b/pkg/xds/server/components.go
index 2b6b107c0..9e05bb62f 100644
--- a/pkg/xds/server/components.go
+++ b/pkg/xds/server/components.go
@@ -32,13 +32,9 @@ var (
 
 func meshResourceTypes(exclude map[core_model.ResourceType]bool) []core_model.ResourceType {
 	types := []core_model.ResourceType{}
-	for _, typ := range registry.Global().ListTypes() {
-		r, err := registry.Global().NewObject(typ)
-		if err != nil {
-			panic(err)
-		}
-		if r.Scope() == core_model.ScopeMesh && !exclude[typ] {
-			types = append(types, typ)
+	for _, desc := range registry.Global().ObjectDescriptors() {
+		if desc.Scope == core_model.ScopeMesh && !exclude[desc.Name] {
+			types = append(types, desc.Name)
 		}
 	}
 	for typ := range HashMeshIncludedGlobalResources {
diff --git a/tools/resource-gen.go b/tools/resource-gen.go
index 9ca64beef..4dc6357db 100644
--- a/tools/resource-gen.go
+++ b/tools/resource-gen.go
@@ -19,73 +19,6 @@ import (
 	_ "github.com/kumahq/kuma/api/mesh/v1alpha1"
 )
 
-var KdsResourceTemplate = template.Must(template.New("kds").Parse(`
-// Generated by tools/resource-gen.
-// Run "make generate" to update this file.
-
-// nolint:whitespace
-package definitions
-
-import (
-	"github.com/kumahq/kuma/pkg/core/resources/apis/{{.Package}}"
-)
-
-{{$pkg := .Package}}
-var {{.Package}}Definitions = []KdsDefinition{
-	{{range .Resources}}{{if not .SkipKds }}
-	{
-		Type: {{$pkg}}.{{.ResourceType}}Type,
-		Direction: {{.KdsDirection}},
-	},
-	{{end}}{{end}}
-}
-`))
-
-var KumactlEntitiesTemplate = template.Must(template.New("kumactl").Parse(`
-// Generated by tools/resource-gen.
-// Run "make generate" to update this file.
-// nolint:whitespace
-package entities
-import (
-	"github.com/kumahq/kuma/pkg/core/resources/apis/{{.Package}}"
-)
-{{$pkg := .Package}}
-var {{.Package}}Entities = []Definition{
-	{{range .Resources}}{{if not .SkipKumactl }}
-	{
-		Singular: "{{.KumactlSingular}}",
-		Plural: "{{.KumactlPlural}}",
-		ResourceType: {{$pkg}}.{{.ResourceType}}Type,
-		ReadOnly: {{.WsReadOnly}},
-	},
-	{{end}}{{end}}
-}
-`))
-
-var WsResourceTemplate = template.Must(template.New("ws").Parse(`
-// Generated by tools/resource-gen.
-// Run "make generate" to update this file.
-
-// nolint:whitespace
-package definitions
-
-import (
-	"github.com/kumahq/kuma/pkg/core/resources/apis/{{.Package}}"
-)
-
-{{$pkg := .Package}}
-var {{.Package}}WsDefinitions = []ResourceWsDefinition{
-	{{range .Resources}}{{if not .SkipWs}}
-	{
-		Type: {{$pkg}}.{{.ResourceType}}Type,
-		Path: "{{.WsPath}}",
-		AdminOnly: {{.WsAdminOnly}},
-		ReadOnly: {{.WsReadOnly}},
-	},
-	{{end}}{{end}}
-}
-`))
-
 // ResourceTemplate for creating a Kuma resource.
 var ResourceTemplate = template.Must(template.New("resource").Parse(`
 // Generated by tools/resource-gen.
@@ -120,10 +53,6 @@ func New{{.ResourceName}}() *{{.ResourceName}} {
 	}
 }
 
-func (t *{{.ResourceName}}) GetType() model.ResourceType {
-	return {{.ResourceType}}Type
-}
-
 func (t *{{.ResourceName}}) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -160,12 +89,8 @@ func (t *{{.ResourceName}}) SetSpec(spec model.ResourceSpec) error {
 	}
 }
 
-func (t *{{.ResourceName}}) Scope() model.ResourceScope {
-{{if .Global}}
-	return model.ScopeGlobal
-{{else}}
-	return model.ScopeMesh
-{{end}}
+func (t *{{.ResourceName}}) Descriptor() model.ResourceTypeDescriptor {
+	return {{.ResourceName}}TypeDescriptor 
 }
 
 var _ model.ResourceList = &{{.ResourceName}}List{}
@@ -204,12 +129,26 @@ func (l *{{.ResourceName}}List) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
-{{if not .SkipRegistration}}
+var {{.ResourceName}}TypeDescriptor = model.ResourceTypeDescriptor{
+		Name: {{.ResourceType}}Type,
+		Resource: New{{.ResourceName}}(),
+		ResourceList: &{{.ResourceName}}List{},
+		ReadOnly: {{.WsReadOnly}},
+		AdminOnly: {{.WsAdminOnly}},
+		Scope: {{if .Global}}model.ScopeGlobal{{else}}model.ScopeMesh{{end}},
+		{{- if ne .KdsDirection ""}}
+		KDSFlags: {{.KdsDirection}},
+		{{- end}}
+		WsPath: "{{.WsPath}}",
+		KumactlArg: "{{.KumactlSingular}}",
+		KumactlListArg: "{{.KumactlPlural}}",
+	}
+
+{{- if not .SkipRegistration}}
 func init() {
-	registry.RegisterType(New{{.ResourceName}}())
-	registry.RegistryListType(&{{.ResourceName}}List{})
+	registry.RegisterType({{.ResourceName}}TypeDescriptor)
 }
-{{end}}
+{{- end}}
 {{end}}
 `))
 
@@ -250,14 +189,11 @@ type ResourceInfo struct {
 	SkipRegistration bool
 	SkipValidation   bool
 	Global           bool
-	SkipKumactl      bool
 	KumactlSingular  string
 	KumactlPlural    string
-	SkipWs           bool
 	WsReadOnly       bool
 	WsAdminOnly      bool
 	WsPath           string
-	SkipKds          bool
 	KdsDirection     string
 }
 
@@ -272,8 +208,6 @@ func ToResourceInfo(m protoreflect.MessageType) ResourceInfo {
 		SkipRegistration: r.SkipRegistration,
 		SkipValidation:   r.SkipValidation,
 		Global:           r.Global,
-		SkipWs:           true,
-		SkipKumactl:      true,
 	}
 	if r.Ws != nil {
 		pluralResourceName := r.Ws.Plural
@@ -283,20 +217,25 @@ func ToResourceInfo(m protoreflect.MessageType) ResourceInfo {
 		out.WsReadOnly = r.Ws.ReadOnly
 		out.WsAdminOnly = r.Ws.AdminOnly
 		out.WsPath = pluralResourceName
-		out.KumactlSingular = r.Ws.Name
-		out.KumactlPlural = pluralResourceName
-		out.SkipWs = out.WsPath == "" || out.SkipRegistration
-		out.SkipKumactl = out.SkipWs || out.WsReadOnly
+		if !r.Ws.ReadOnly {
+			out.KumactlSingular = r.Ws.Name
+			out.KumactlPlural = pluralResourceName
+			// Keep the typo to preserve backward compatibility
+			if out.KumactlSingular == "health-check" {
+				out.KumactlSingular = "healthcheck"
+				out.KumactlPlural = "healthchecks"
+			}
+		}
 	}
 	switch {
 	case r.Kds == nil || (!r.Kds.SendToZone && !r.Kds.SendToGlobal):
-		out.SkipKds = true
+		out.KdsDirection = ""
 	case r.Kds.SendToGlobal && r.Kds.SendToZone:
-		out.KdsDirection = "FromZoneToGlobal | FromGlobalToZone"
+		out.KdsDirection = "model.FromZoneToGlobal | model.FromGlobalToZone"
 	case r.Kds.SendToGlobal:
-		out.KdsDirection = "FromZoneToGlobal"
+		out.KdsDirection = "model.FromZoneToGlobal"
 	case r.Kds.SendToZone:
-		out.KdsDirection = "FromGlobalToZone"
+		out.KdsDirection = "model.FromGlobalToZone"
 	}
 
 	if p := m.Descriptor().Parent(); p != nil {
@@ -355,12 +294,6 @@ func main() {
 		switch gen {
 		case "type":
 			globalTemplates[path.Join(apisPath, "generated_resources.go")] = ResourceTemplate
-		case "ws":
-			globalTemplates[path.Join("pkg/api-server/definitions", fmt.Sprintf("generated_%s_definitions.go", pkg))] = WsResourceTemplate
-		case "kds":
-			globalTemplates[path.Join("pkg/kds/definitions", fmt.Sprintf("generated_%s_definitions.go", pkg))] = KdsResourceTemplate
-		case "kumactl":
-			globalTemplates[path.Join("app/kumactl/pkg/entities", fmt.Sprintf("generated_%s_entities.go", pkg))] = KumactlEntitiesTemplate
 		default:
 			panic(fmt.Sprintf("%s is not a valid generator option\n", gen))
 		}


Test patch:
diff --git a/app/kumactl/cmd/apply/apply_test.go b/app/kumactl/cmd/apply/apply_test.go
index 6e46056f9..12b66b5d1 100644
--- a/app/kumactl/cmd/apply/apply_test.go
+++ b/app/kumactl/cmd/apply/apply_test.go
@@ -23,6 +23,7 @@ import (
 	"github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	"github.com/kumahq/kuma/pkg/core/resources/apis/system"
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
+	"github.com/kumahq/kuma/pkg/core/resources/registry"
 	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
 	"github.com/kumahq/kuma/pkg/core/rest/errors/types"
 	memory_resources "github.com/kumahq/kuma/pkg/plugins/resources/memory"
@@ -39,6 +40,7 @@ var _ = Describe("kumactl apply", func() {
 	BeforeEach(func() {
 		rootCtx = &kumactl_cmd.RootContext{
 			Runtime: kumactl_cmd.RootRuntime{
+				Registry: registry.Global(),
 				NewResourceStore: func(*config_proto.ControlPlaneCoordinates_ApiServer) (core_store.ResourceStore, error) {
 					return store, nil
 				},
diff --git a/app/kumactl/cmd/generate/generate_dataplane_token_test.go b/app/kumactl/cmd/generate/generate_dataplane_token_test.go
index 51a9b5ff1..e891c6a5a 100644
--- a/app/kumactl/cmd/generate/generate_dataplane_token_test.go
+++ b/app/kumactl/cmd/generate/generate_dataplane_token_test.go
@@ -15,6 +15,7 @@ import (
 	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
 	"github.com/kumahq/kuma/app/kumactl/pkg/tokens"
 	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
+	"github.com/kumahq/kuma/pkg/core/resources/registry"
 	"github.com/kumahq/kuma/pkg/util/test"
 )
 
@@ -41,6 +42,7 @@ var _ = Describe("kumactl generate dataplane-token", func() {
 		generator = &staticDataplaneTokenGenerator{}
 		ctx = &kumactl_cmd.RootContext{
 			Runtime: kumactl_cmd.RootRuntime{
+				Registry: registry.NewTypeRegistry(),
 				NewDataplaneTokenClient: func(*config_proto.ControlPlaneCoordinates_ApiServer) (tokens.DataplaneTokenClient, error) {
 					return generator, nil
 				},
diff --git a/app/kumactl/cmd/generate/generate_zoneingress_token_test.go b/app/kumactl/cmd/generate/generate_zoneingress_token_test.go
index e1c2d1716..bd90bb73f 100644
--- a/app/kumactl/cmd/generate/generate_zoneingress_token_test.go
+++ b/app/kumactl/cmd/generate/generate_zoneingress_token_test.go
@@ -14,6 +14,7 @@ import (
 	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
 	"github.com/kumahq/kuma/app/kumactl/pkg/tokens"
 	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
+	"github.com/kumahq/kuma/pkg/core/resources/registry"
 	"github.com/kumahq/kuma/pkg/util/test"
 )
 
@@ -41,6 +42,7 @@ var _ = Describe("kumactl generate zone-ingress-token", func() {
 		generator = &staticZoneIngressTokenGenerator{}
 		ctx = &kumactl_cmd.RootContext{
 			Runtime: kumactl_cmd.RootRuntime{
+				Registry: registry.NewTypeRegistry(),
 				NewZoneIngressTokenClient: func(*config_proto.ControlPlaneCoordinates_ApiServer) (tokens.ZoneIngressTokenClient, error) {
 					return generator, nil
 				},
diff --git a/app/kumactl/cmd/get/get_circuit_breakers_test.go b/app/kumactl/cmd/get/get_circuit_breakers_test.go
index ac22e565c..76526b497 100644
--- a/app/kumactl/cmd/get/get_circuit_breakers_test.go
+++ b/app/kumactl/cmd/get/get_circuit_breakers_test.go
@@ -18,14 +18,12 @@ import (
 
 	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
 	"github.com/kumahq/kuma/app/kumactl/cmd"
-	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
-	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
 	"github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
 	memory_resources "github.com/kumahq/kuma/pkg/plugins/resources/memory"
+	test_kumactl "github.com/kumahq/kuma/pkg/test/kumactl"
 	test_model "github.com/kumahq/kuma/pkg/test/resources/model"
-	"github.com/kumahq/kuma/pkg/util/test"
 )
 
 var _ = Describe("kumactl get circuit-breakers", func() {
@@ -115,25 +113,17 @@ var _ = Describe("kumactl get circuit-breakers", func() {
 
 	Describe("GetCircuitBreakerCmd", func() {
 
-		var rootCtx *kumactl_cmd.RootContext
 		var rootCmd *cobra.Command
 		var buf *bytes.Buffer
 		var store core_store.ResourceStore
 		rootTime, _ := time.Parse(time.RFC3339, "2008-04-27T16:05:36.995Z")
 		BeforeEach(func() {
 			// setup
-			rootCtx = &kumactl_cmd.RootContext{
-				Runtime: kumactl_cmd.RootRuntime{
-					Now: func() time.Time { return rootTime },
-					NewResourceStore: func(*config_proto.ControlPlaneCoordinates_ApiServer) (core_store.ResourceStore, error) {
-						return store, nil
-					},
-					NewAPIServerClient: test.GetMockNewAPIServerClient(),
-				},
-			}
-
 			store = core_store.NewPaginationStore(memory_resources.NewStore())
 
+			rootCtx, err := test_kumactl.MakeRootContext(rootTime, store, mesh.CircuitBreakerResourceTypeDescriptor)
+			Expect(err).ToNot(HaveOccurred())
+
 			for _, cb := range circuitBreakerResources {
 				err := store.Create(context.Background(), cb, core_store.CreateBy(core_model.MetaToResourceKey(cb.GetMeta())))
 				Expect(err).ToNot(HaveOccurred())
diff --git a/app/kumactl/cmd/get/get_dataplanes_test.go b/app/kumactl/cmd/get/get_dataplanes_test.go
index ac1a26f3d..c48136a6e 100644
--- a/app/kumactl/cmd/get/get_dataplanes_test.go
+++ b/app/kumactl/cmd/get/get_dataplanes_test.go
@@ -16,14 +16,12 @@ import (
 
 	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
 	"github.com/kumahq/kuma/app/kumactl/cmd"
-	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
-	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
 	core_mesh "github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
 	memory_resources "github.com/kumahq/kuma/pkg/plugins/resources/memory"
+	test_kumactl "github.com/kumahq/kuma/pkg/test/kumactl"
 	test_model "github.com/kumahq/kuma/pkg/test/resources/model"
-	"github.com/kumahq/kuma/pkg/util/test"
 )
 
 var _ = Describe("kumactl get dataplanes", func() {
@@ -87,26 +85,17 @@ var _ = Describe("kumactl get dataplanes", func() {
 
 	Describe("GetDataplanesCmd", func() {
 
-		var rootCtx *kumactl_cmd.RootContext
 		var rootCmd *cobra.Command
 		var buf *bytes.Buffer
 		var store core_store.ResourceStore
 		rootTime, _ := time.Parse(time.RFC3339, "2008-04-27T16:05:36.995Z")
 		BeforeEach(func() {
 			// setup
-
-			rootCtx = &kumactl_cmd.RootContext{
-				Runtime: kumactl_cmd.RootRuntime{
-					Now: func() time.Time { return rootTime },
-					NewResourceStore: func(*config_proto.ControlPlaneCoordinates_ApiServer) (core_store.ResourceStore, error) {
-						return store, nil
-					},
-					NewAPIServerClient: test.GetMockNewAPIServerClient(),
-				},
-			}
-
 			store = core_store.NewPaginationStore(memory_resources.NewStore())
 
+			rootCtx, err := test_kumactl.MakeRootContext(rootTime, store, core_mesh.DataplaneResourceTypeDescriptor)
+			Expect(err).ToNot(HaveOccurred())
+
 			for _, pt := range dataplanes {
 				key := core_model.ResourceKey{
 					Mesh: pt.Meta.GetMesh(),
diff --git a/app/kumactl/cmd/get/get_external_services_test.go b/app/kumactl/cmd/get/get_external_services_test.go
index 06aec15c1..80b4ec197 100644
--- a/app/kumactl/cmd/get/get_external_services_test.go
+++ b/app/kumactl/cmd/get/get_external_services_test.go
@@ -16,14 +16,12 @@ import (
 
 	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
 	"github.com/kumahq/kuma/app/kumactl/cmd"
-	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
-	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
 	core_mesh "github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
 	memory_resources "github.com/kumahq/kuma/pkg/plugins/resources/memory"
+	test_kumactl "github.com/kumahq/kuma/pkg/test/kumactl"
 	test_model "github.com/kumahq/kuma/pkg/test/resources/model"
-	"github.com/kumahq/kuma/pkg/util/test"
 )
 
 var _ = Describe("kumactl get external-services", func() {
@@ -67,26 +65,17 @@ var _ = Describe("kumactl get external-services", func() {
 
 	Describe("GetExternalServicesCmd", func() {
 
-		var rootCtx *kumactl_cmd.RootContext
 		var rootCmd *cobra.Command
 		var buf *bytes.Buffer
 		var store core_store.ResourceStore
 		rootTime, _ := time.Parse(time.RFC3339, "2008-04-27T16:05:36.995Z")
 		BeforeEach(func() {
 			// setup
-
-			rootCtx = &kumactl_cmd.RootContext{
-				Runtime: kumactl_cmd.RootRuntime{
-					Now: func() time.Time { return rootTime },
-					NewResourceStore: func(*config_proto.ControlPlaneCoordinates_ApiServer) (core_store.ResourceStore, error) {
-						return store, nil
-					},
-					NewAPIServerClient: test.GetMockNewAPIServerClient(),
-				},
-			}
-
 			store = core_store.NewPaginationStore(memory_resources.NewStore())
 
+			rootCtx, err := test_kumactl.MakeRootContext(rootTime, store, core_mesh.ExternalServiceResourceTypeDescriptor)
+			Expect(err).ToNot(HaveOccurred())
+
 			for _, pt := range externalServices {
 				key := core_model.ResourceKey{
 					Mesh: pt.Meta.GetMesh(),
diff --git a/app/kumactl/cmd/get/get_fault_injections_test.go b/app/kumactl/cmd/get/get_fault_injections_test.go
index 5f04919df..a529c63fa 100644
--- a/app/kumactl/cmd/get/get_fault_injections_test.go
+++ b/app/kumactl/cmd/get/get_fault_injections_test.go
@@ -18,14 +18,12 @@ import (
 
 	"github.com/kumahq/kuma/api/mesh/v1alpha1"
 	"github.com/kumahq/kuma/app/kumactl/cmd"
-	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
-	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
 	"github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
 	memory_resources "github.com/kumahq/kuma/pkg/plugins/resources/memory"
+	test_kumactl "github.com/kumahq/kuma/pkg/test/kumactl"
 	test_model "github.com/kumahq/kuma/pkg/test/resources/model"
-	"github.com/kumahq/kuma/pkg/util/test"
 )
 
 var _ = Describe("kumactl get fault-injections", func() {
@@ -109,25 +107,17 @@ var _ = Describe("kumactl get fault-injections", func() {
 
 	Describe("GetFaultInjectionCmd", func() {
 
-		var rootCtx *kumactl_cmd.RootContext
 		var rootCmd *cobra.Command
 		var buf *bytes.Buffer
 		var store core_store.ResourceStore
 		rootTime, _ := time.Parse(time.RFC3339, "2008-04-27T16:05:36.995Z")
 		BeforeEach(func() {
 			// setup
-			rootCtx = &kumactl_cmd.RootContext{
-				Runtime: kumactl_cmd.RootRuntime{
-					Now: func() time.Time { return rootTime },
-					NewResourceStore: func(*config_proto.ControlPlaneCoordinates_ApiServer) (core_store.ResourceStore, error) {
-						return store, nil
-					},
-					NewAPIServerClient: test.GetMockNewAPIServerClient(),
-				},
-			}
-
 			store = core_store.NewPaginationStore(memory_resources.NewStore())
 
+			rootCtx, err := test_kumactl.MakeRootContext(rootTime, store, mesh.FaultInjectionResourceTypeDescriptor)
+			Expect(err).ToNot(HaveOccurred())
+
 			for _, ds := range faultInjectionResources {
 				err := store.Create(context.Background(), ds, core_store.CreateBy(core_model.MetaToResourceKey(ds.GetMeta())))
 				Expect(err).ToNot(HaveOccurred())
diff --git a/app/kumactl/cmd/get/get_global_secrets_test.go b/app/kumactl/cmd/get/get_global_secrets_test.go
index ceb45b5fb..0ca5949f2 100644
--- a/app/kumactl/cmd/get/get_global_secrets_test.go
+++ b/app/kumactl/cmd/get/get_global_secrets_test.go
@@ -14,15 +14,13 @@ import (
 
 	system_proto "github.com/kumahq/kuma/api/system/v1alpha1"
 	"github.com/kumahq/kuma/app/kumactl/cmd"
-	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
-	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
 	"github.com/kumahq/kuma/pkg/core/resources/apis/system"
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
 	memory_resources "github.com/kumahq/kuma/pkg/plugins/resources/memory"
+	test_kumactl "github.com/kumahq/kuma/pkg/test/kumactl"
 	. "github.com/kumahq/kuma/pkg/test/matchers"
 	test_model "github.com/kumahq/kuma/pkg/test/resources/model"
-	"github.com/kumahq/kuma/pkg/util/test"
 )
 
 var _ = Describe("kumactl get global-secrets", func() {
@@ -58,25 +56,17 @@ var _ = Describe("kumactl get global-secrets", func() {
 
 	Describe("GetGlobalSecretsCmd", func() {
 
-		var rootCtx *kumactl_cmd.RootContext
 		var rootCmd *cobra.Command
 		var buf *bytes.Buffer
 		var store core_store.ResourceStore
 		rootTime, _ := time.Parse(time.RFC3339, "2008-04-27T16:05:36.995Z")
 		BeforeEach(func() {
 			// setup
-			rootCtx = &kumactl_cmd.RootContext{
-				Runtime: kumactl_cmd.RootRuntime{
-					Now: func() time.Time { return rootTime },
-					NewResourceStore: func(*config_proto.ControlPlaneCoordinates_ApiServer) (core_store.ResourceStore, error) {
-						return store, nil
-					},
-					NewAPIServerClient: test.GetMockNewAPIServerClient(),
-				},
-			}
-
 			store = core_store.NewPaginationStore(memory_resources.NewStore())
 
+			rootCtx, err := test_kumactl.MakeRootContext(rootTime, store, system.GlobalSecretResourceTypeDescriptor)
+			Expect(err).ToNot(HaveOccurred())
+
 			for _, pt := range sampleSecrets {
 				key := core_model.ResourceKey{
 					Mesh: pt.Meta.GetMesh(),
diff --git a/app/kumactl/cmd/get/get_healthchecks_test.go b/app/kumactl/cmd/get/get_healthchecks_test.go
index a0ce150d8..e10dec660 100644
--- a/app/kumactl/cmd/get/get_healthchecks_test.go
+++ b/app/kumactl/cmd/get/get_healthchecks_test.go
@@ -16,14 +16,12 @@ import (
 
 	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
 	"github.com/kumahq/kuma/app/kumactl/cmd"
-	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
-	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
 	core_mesh "github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
 	memory_resources "github.com/kumahq/kuma/pkg/plugins/resources/memory"
+	test_kumactl "github.com/kumahq/kuma/pkg/test/kumactl"
 	test_model "github.com/kumahq/kuma/pkg/test/resources/model"
-	"github.com/kumahq/kuma/pkg/util/test"
 )
 
 var _ = Describe("kumactl get healthchecks", func() {
@@ -57,25 +55,17 @@ var _ = Describe("kumactl get healthchecks", func() {
 
 	Describe("GetHealthChecksCmd", func() {
 
-		var rootCtx *kumactl_cmd.RootContext
 		var rootCmd *cobra.Command
 		var buf *bytes.Buffer
 		var store core_store.ResourceStore
 		rootTime, _ := time.Parse(time.RFC3339, "2008-04-27T16:05:36.995Z")
 		BeforeEach(func() {
 			// setup
-			rootCtx = &kumactl_cmd.RootContext{
-				Runtime: kumactl_cmd.RootRuntime{
-					Now: func() time.Time { return rootTime },
-					NewResourceStore: func(*config_proto.ControlPlaneCoordinates_ApiServer) (core_store.ResourceStore, error) {
-						return store, nil
-					},
-					NewAPIServerClient: test.GetMockNewAPIServerClient(),
-				},
-			}
-
 			store = core_store.NewPaginationStore(memory_resources.NewStore())
 
+			rootCtx, err := test_kumactl.MakeRootContext(rootTime, store, core_mesh.HealthCheckResourceTypeDescriptor)
+			Expect(err).ToNot(HaveOccurred())
+
 			for _, pt := range sampleHealthChecks {
 				key := core_model.ResourceKey{
 					Mesh: pt.Meta.GetMesh(),
diff --git a/app/kumactl/cmd/get/get_meshes_test.go b/app/kumactl/cmd/get/get_meshes_test.go
index 2194972bb..b63db2b4d 100644
--- a/app/kumactl/cmd/get/get_meshes_test.go
+++ b/app/kumactl/cmd/get/get_meshes_test.go
@@ -16,15 +16,13 @@ import (
 
 	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
 	"github.com/kumahq/kuma/app/kumactl/cmd"
-	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
-	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
 	"github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
 	memory_resources "github.com/kumahq/kuma/pkg/plugins/resources/memory"
+	test_kumactl "github.com/kumahq/kuma/pkg/test/kumactl"
 	test_model "github.com/kumahq/kuma/pkg/test/resources/model"
 	util_proto "github.com/kumahq/kuma/pkg/util/proto"
-	"github.com/kumahq/kuma/pkg/util/test"
 )
 
 var _ = Describe("kumactl get meshes", func() {
@@ -130,25 +128,17 @@ var _ = Describe("kumactl get meshes", func() {
 
 	Describe("GetMeshesCmd", func() {
 
-		var rootCtx *kumactl_cmd.RootContext
 		var rootCmd *cobra.Command
 		var buf *bytes.Buffer
 		var store core_store.ResourceStore
 		rootTime, _ := time.Parse(time.RFC3339, "2008-04-27T16:05:36.995Z")
 		BeforeEach(func() {
 			// setup
-			rootCtx = &kumactl_cmd.RootContext{
-				Runtime: kumactl_cmd.RootRuntime{
-					Now: func() time.Time { return rootTime },
-					NewResourceStore: func(*config_proto.ControlPlaneCoordinates_ApiServer) (core_store.ResourceStore, error) {
-						return store, nil
-					},
-					NewAPIServerClient: test.GetMockNewAPIServerClient(),
-				},
-			}
-
 			store = core_store.NewPaginationStore(memory_resources.NewStore())
 
+			rootCtx, err := test_kumactl.MakeRootContext(rootTime, store, mesh.MeshResourceTypeDescriptor)
+			Expect(err).ToNot(HaveOccurred())
+
 			for _, ds := range sampleMeshes {
 				key := core_model.ResourceKey{
 					Mesh: ds.Meta.GetMesh(),
diff --git a/app/kumactl/cmd/get/get_proxytemplates_test.go b/app/kumactl/cmd/get/get_proxytemplates_test.go
index 3cb1c3e9b..687216e73 100644
--- a/app/kumactl/cmd/get/get_proxytemplates_test.go
+++ b/app/kumactl/cmd/get/get_proxytemplates_test.go
@@ -16,14 +16,12 @@ import (
 
 	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
 	"github.com/kumahq/kuma/app/kumactl/cmd"
-	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
-	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
 	core_mesh "github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
 	memory_resources "github.com/kumahq/kuma/pkg/plugins/resources/memory"
+	test_kumactl "github.com/kumahq/kuma/pkg/test/kumactl"
 	test_model "github.com/kumahq/kuma/pkg/test/resources/model"
-	"github.com/kumahq/kuma/pkg/util/test"
 )
 
 var _ = Describe("kumactl get proxytemplates", func() {
@@ -58,25 +56,17 @@ var _ = Describe("kumactl get proxytemplates", func() {
 
 	Describe("GetProxyTemplatesCmd", func() {
 
-		var rootCtx *kumactl_cmd.RootContext
 		var rootCmd *cobra.Command
 		var buf *bytes.Buffer
 		var store core_store.ResourceStore
 		rootTime, _ := time.Parse(time.RFC3339, "2008-04-27T16:05:36.995Z")
 		BeforeEach(func() {
 			// setup
-			rootCtx = &kumactl_cmd.RootContext{
-				Runtime: kumactl_cmd.RootRuntime{
-					Now: func() time.Time { return rootTime },
-					NewResourceStore: func(*config_proto.ControlPlaneCoordinates_ApiServer) (core_store.ResourceStore, error) {
-						return store, nil
-					},
-					NewAPIServerClient: test.GetMockNewAPIServerClient(),
-				},
-			}
-
 			store = core_store.NewPaginationStore(memory_resources.NewStore())
 
+			rootCtx, err := test_kumactl.MakeRootContext(rootTime, store, core_mesh.ProxyTemplateResourceTypeDescriptor)
+			Expect(err).ToNot(HaveOccurred())
+
 			for _, pt := range sampleProxyTemplates {
 				key := core_model.ResourceKey{
 					Mesh: pt.Meta.GetMesh(),
diff --git a/app/kumactl/cmd/get/get_ratelimits_test.go b/app/kumactl/cmd/get/get_ratelimits_test.go
index d20b37525..b9054093c 100644
--- a/app/kumactl/cmd/get/get_ratelimits_test.go
+++ b/app/kumactl/cmd/get/get_ratelimits_test.go
@@ -16,14 +16,12 @@ import (
 
 	"github.com/kumahq/kuma/api/mesh/v1alpha1"
 	"github.com/kumahq/kuma/app/kumactl/cmd"
-	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
-	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
 	"github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
 	memory_resources "github.com/kumahq/kuma/pkg/plugins/resources/memory"
+	test_kumactl "github.com/kumahq/kuma/pkg/test/kumactl"
 	test_model "github.com/kumahq/kuma/pkg/test/resources/model"
-	"github.com/kumahq/kuma/pkg/util/test"
 )
 
 var _ = Describe("kumactl get rate-limits", func() {
@@ -81,25 +79,17 @@ var _ = Describe("kumactl get rate-limits", func() {
 
 	Describe("GetRateLimitsCmd", func() {
 
-		var rootCtx *kumactl_cmd.RootContext
 		var rootCmd *cobra.Command
 		var buf *bytes.Buffer
 		var store core_store.ResourceStore
 		rootTime, _ := time.Parse(time.RFC3339, "2008-04-27T16:05:36.995Z")
 		BeforeEach(func() {
 			// setup
-			rootCtx = &kumactl_cmd.RootContext{
-				Runtime: kumactl_cmd.RootRuntime{
-					Now: func() time.Time { return rootTime },
-					NewResourceStore: func(*config_proto.ControlPlaneCoordinates_ApiServer) (core_store.ResourceStore, error) {
-						return store, nil
-					},
-					NewAPIServerClient: test.GetMockNewAPIServerClient(),
-				},
-			}
-
 			store = core_store.NewPaginationStore(memory_resources.NewStore())
 
+			rootCtx, err := test_kumactl.MakeRootContext(rootTime, store, mesh.RateLimitResourceTypeDescriptor)
+			Expect(err).ToNot(HaveOccurred())
+
 			for _, ds := range rateLimitResources {
 				err := store.Create(context.Background(), ds, core_store.CreateBy(core_model.MetaToResourceKey(ds.GetMeta())))
 				Expect(err).ToNot(HaveOccurred())
diff --git a/app/kumactl/cmd/get/get_retries_test.go b/app/kumactl/cmd/get/get_retries_test.go
index a57569a0a..59dee0758 100644
--- a/app/kumactl/cmd/get/get_retries_test.go
+++ b/app/kumactl/cmd/get/get_retries_test.go
@@ -16,14 +16,12 @@ import (
 
 	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
 	"github.com/kumahq/kuma/app/kumactl/cmd"
-	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
-	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
 	core_mesh "github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
 	memory_resources "github.com/kumahq/kuma/pkg/plugins/resources/memory"
+	test_kumactl "github.com/kumahq/kuma/pkg/test/kumactl"
 	test_model "github.com/kumahq/kuma/pkg/test/resources/model"
-	"github.com/kumahq/kuma/pkg/util/test"
 )
 
 var _ = Describe("kumactl get retries", func() {
@@ -57,7 +55,6 @@ var _ = Describe("kumactl get retries", func() {
 
 	Describe("GetRetriesCmd", func() {
 		var (
-			rootCtx *kumactl_cmd.RootContext
 			rootCmd *cobra.Command
 			buf     *bytes.Buffer
 			store   core_store.ResourceStore
@@ -66,20 +63,11 @@ var _ = Describe("kumactl get retries", func() {
 
 		BeforeEach(func() {
 			// setup
-			rootCtx = &kumactl_cmd.RootContext{
-				Runtime: kumactl_cmd.RootRuntime{
-					Now: func() time.Time { return rootTime },
-					NewResourceStore: func(
-						*config_proto.ControlPlaneCoordinates_ApiServer,
-					) (core_store.ResourceStore, error) {
-						return store, nil
-					},
-					NewAPIServerClient: test.GetMockNewAPIServerClient(),
-				},
-			}
-
 			store = core_store.NewPaginationStore(memory_resources.NewStore())
 
+			rootCtx, err := test_kumactl.MakeRootContext(rootTime, store, core_mesh.RetryResourceTypeDescriptor)
+			Expect(err).ToNot(HaveOccurred())
+
 			for _, pt := range sampleRetries {
 				key := core_model.ResourceKey{
 					Mesh: pt.Meta.GetMesh(),
diff --git a/app/kumactl/cmd/get/get_secrets_test.go b/app/kumactl/cmd/get/get_secrets_test.go
index f670b5e90..26eb01e3f 100644
--- a/app/kumactl/cmd/get/get_secrets_test.go
+++ b/app/kumactl/cmd/get/get_secrets_test.go
@@ -17,14 +17,12 @@ import (
 
 	system_proto "github.com/kumahq/kuma/api/system/v1alpha1"
 	"github.com/kumahq/kuma/app/kumactl/cmd"
-	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
-	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
 	"github.com/kumahq/kuma/pkg/core/resources/apis/system"
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
 	memory_resources "github.com/kumahq/kuma/pkg/plugins/resources/memory"
+	test_kumactl "github.com/kumahq/kuma/pkg/test/kumactl"
 	test_model "github.com/kumahq/kuma/pkg/test/resources/model"
-	"github.com/kumahq/kuma/pkg/util/test"
 )
 
 var _ = Describe("kumactl get secrets", func() {
@@ -60,25 +58,17 @@ var _ = Describe("kumactl get secrets", func() {
 
 	Describe("GetSecretsCmd", func() {
 
-		var rootCtx *kumactl_cmd.RootContext
 		var rootCmd *cobra.Command
 		var buf *bytes.Buffer
 		var store core_store.ResourceStore
 		rootTime, _ := time.Parse(time.RFC3339, "2008-04-27T16:05:36.995Z")
 		BeforeEach(func() {
 			// setup
-			rootCtx = &kumactl_cmd.RootContext{
-				Runtime: kumactl_cmd.RootRuntime{
-					Now: func() time.Time { return rootTime },
-					NewResourceStore: func(*config_proto.ControlPlaneCoordinates_ApiServer) (core_store.ResourceStore, error) {
-						return store, nil
-					},
-					NewAPIServerClient: test.GetMockNewAPIServerClient(),
-				},
-			}
-
 			store = core_store.NewPaginationStore(memory_resources.NewStore())
 
+			rootCtx, err := test_kumactl.MakeRootContext(rootTime, store, system.SecretResourceTypeDescriptor)
+			Expect(err).ToNot(HaveOccurred())
+
 			for _, pt := range sampleSecrets {
 				key := core_model.ResourceKey{
 					Mesh: pt.Meta.GetMesh(),
diff --git a/app/kumactl/cmd/get/get_single_resource_test.go b/app/kumactl/cmd/get/get_single_resource_test.go
index 51519ddad..3d8bc583e 100644
--- a/app/kumactl/cmd/get/get_single_resource_test.go
+++ b/app/kumactl/cmd/get/get_single_resource_test.go
@@ -17,6 +17,7 @@ import (
 	"github.com/kumahq/kuma/app/kumactl/pkg/resources"
 	"github.com/kumahq/kuma/pkg/api-server/types"
 	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
+	"github.com/kumahq/kuma/pkg/core/resources/registry"
 	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
 	memory_resources "github.com/kumahq/kuma/pkg/plugins/resources/memory"
 	. "github.com/kumahq/kuma/pkg/test/matchers"
@@ -34,7 +35,6 @@ func (c *testApiServerClient) GetVersion(_ context.Context) (*types.IndexRespons
 }
 
 var _ = Describe("kumactl get [resource] NAME", func() {
-	var rootCtx *kumactl_cmd.RootContext
 	var rootCmd *cobra.Command
 	var outbuf *bytes.Buffer
 	var store core_store.ResourceStore
@@ -42,9 +42,10 @@ var _ = Describe("kumactl get [resource] NAME", func() {
 	rootTime, _ := time.Parse(time.RFC3339, "2008-04-01T16:05:36.995Z")
 	var _ resources.ApiServerClient = &testApiServerClient{}
 	BeforeEach(func() {
-		rootCtx = &kumactl_cmd.RootContext{
+		rootCtx := &kumactl_cmd.RootContext{
 			Runtime: kumactl_cmd.RootRuntime{
-				Now: func() time.Time { return rootTime },
+				Registry: registry.Global(),
+				Now:      func() time.Time { return rootTime },
 				NewResourceStore: func(*config_proto.ControlPlaneCoordinates_ApiServer) (core_store.ResourceStore, error) {
 					return store, nil
 				},
diff --git a/app/kumactl/cmd/get/get_test.go b/app/kumactl/cmd/get/get_test.go
index d50ed93dc..22c065a31 100644
--- a/app/kumactl/cmd/get/get_test.go
+++ b/app/kumactl/cmd/get/get_test.go
@@ -7,8 +7,9 @@ import (
 
 	"github.com/kumahq/kuma/app/kumactl/cmd"
 	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
-	"github.com/kumahq/kuma/app/kumactl/pkg/entities"
 	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
+	"github.com/kumahq/kuma/pkg/core/resources/model"
+	"github.com/kumahq/kuma/pkg/core/resources/registry"
 	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
 	memory_resources "github.com/kumahq/kuma/pkg/plugins/resources/memory"
 )
@@ -31,12 +32,12 @@ var _ = Describe("kumactl get ", func() {
 
 		BeforeEach(func() {
 			// setup
+			store = core_store.NewPaginationStore(memory_resources.NewStore())
+
 			rootCtx = kumactl_cmd.DefaultRootContext()
 			rootCtx.Runtime.NewResourceStore = func(*config_proto.ControlPlaneCoordinates_ApiServer) (core_store.ResourceStore, error) {
 				return store, nil
 			}
-			store = core_store.NewPaginationStore(memory_resources.NewStore())
-
 			rootCmd = cmd.NewRootCmd(rootCtx)
 			for _, cmd := range rootCmd.Commands() {
 				if cmd.Use == "get" {
@@ -49,11 +50,12 @@ var _ = Describe("kumactl get ", func() {
 
 		It("should have get commands for all defined types", func() {
 			// when
-			Expect(len(getCmd.Commands()) > len(entities.Names)).To(BeTrue())
+			all := registry.Global().ObjectDescriptors(model.HasKumactlEnabled())
+			Expect(len(getCmd.Commands()) > len(all)).To(BeTrue())
 
 			// then
-			for _, sub := range entities.All {
-				Expect(hasSubCommand(getCmd, sub.Singular+" NAME")).To(BeTrue(), "failing to find "+sub.Singular)
+			for _, sub := range all {
+				Expect(hasSubCommand(getCmd, sub.KumactlArg+" NAME")).To(BeTrue(), "failing to find "+sub.KumactlArg)
 			}
 		})
 	})
diff --git a/app/kumactl/cmd/get/get_traffic_logs_test.go b/app/kumactl/cmd/get/get_traffic_logs_test.go
index 1741d5cde..7aba6433e 100644
--- a/app/kumactl/cmd/get/get_traffic_logs_test.go
+++ b/app/kumactl/cmd/get/get_traffic_logs_test.go
@@ -16,14 +16,12 @@ import (
 
 	"github.com/kumahq/kuma/api/mesh/v1alpha1"
 	"github.com/kumahq/kuma/app/kumactl/cmd"
-	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
-	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
 	"github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
 	memory_resources "github.com/kumahq/kuma/pkg/plugins/resources/memory"
+	test_kumactl "github.com/kumahq/kuma/pkg/test/kumactl"
 	test_model "github.com/kumahq/kuma/pkg/test/resources/model"
-	"github.com/kumahq/kuma/pkg/util/test"
 )
 
 var _ = Describe("kumactl get traffic-logs", func() {
@@ -87,25 +85,17 @@ var _ = Describe("kumactl get traffic-logs", func() {
 
 	Describe("GetTrafficLogCmd", func() {
 
-		var rootCtx *kumactl_cmd.RootContext
 		var rootCmd *cobra.Command
 		var buf *bytes.Buffer
 		var store core_store.ResourceStore
 		rootTime, _ := time.Parse(time.RFC3339, "2008-04-27T16:05:36.995Z")
 		BeforeEach(func() {
 			// setup
-			rootCtx = &kumactl_cmd.RootContext{
-				Runtime: kumactl_cmd.RootRuntime{
-					Now: func() time.Time { return rootTime },
-					NewResourceStore: func(*config_proto.ControlPlaneCoordinates_ApiServer) (core_store.ResourceStore, error) {
-						return store, nil
-					},
-					NewAPIServerClient: test.GetMockNewAPIServerClient(),
-				},
-			}
-
 			store = core_store.NewPaginationStore(memory_resources.NewStore())
 
+			rootCtx, err := test_kumactl.MakeRootContext(rootTime, store, mesh.TrafficLogResourceTypeDescriptor)
+			Expect(err).ToNot(HaveOccurred())
+
 			for _, ds := range trafficLoggingResources {
 				err := store.Create(context.Background(), ds, core_store.CreateBy(core_model.MetaToResourceKey(ds.GetMeta())))
 				Expect(err).ToNot(HaveOccurred())
diff --git a/app/kumactl/cmd/get/get_traffic_routes_test.go b/app/kumactl/cmd/get/get_traffic_routes_test.go
index 901a4714d..9a30bee50 100644
--- a/app/kumactl/cmd/get/get_traffic_routes_test.go
+++ b/app/kumactl/cmd/get/get_traffic_routes_test.go
@@ -16,14 +16,12 @@ import (
 
 	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
 	"github.com/kumahq/kuma/app/kumactl/cmd"
-	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
-	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
 	core_mesh "github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
 	memory_resources "github.com/kumahq/kuma/pkg/plugins/resources/memory"
+	test_kumactl "github.com/kumahq/kuma/pkg/test/kumactl"
 	test_model "github.com/kumahq/kuma/pkg/test/resources/model"
-	"github.com/kumahq/kuma/pkg/util/test"
 )
 
 var _ = Describe("kumactl get traffic-routes", func() {
@@ -58,25 +56,17 @@ var _ = Describe("kumactl get traffic-routes", func() {
 
 	Describe("GetTrafficRoutesCmd", func() {
 
-		var rootCtx *kumactl_cmd.RootContext
 		var rootCmd *cobra.Command
 		var buf *bytes.Buffer
 		var store core_store.ResourceStore
 		rootTime, _ := time.Parse(time.RFC3339, "2008-04-27T16:05:36.995Z")
 		BeforeEach(func() {
 			// setup
-			rootCtx = &kumactl_cmd.RootContext{
-				Runtime: kumactl_cmd.RootRuntime{
-					Now: func() time.Time { return rootTime },
-					NewResourceStore: func(*config_proto.ControlPlaneCoordinates_ApiServer) (core_store.ResourceStore, error) {
-						return store, nil
-					},
-					NewAPIServerClient: test.GetMockNewAPIServerClient(),
-				},
-			}
-
 			store = core_store.NewPaginationStore(memory_resources.NewStore())
 
+			rootCtx, err := test_kumactl.MakeRootContext(rootTime, store, core_mesh.TrafficRouteResourceTypeDescriptor)
+			Expect(err).ToNot(HaveOccurred())
+
 			for _, pt := range sampleTrafficRoutes {
 				key := core_model.ResourceKey{
 					Mesh: pt.Meta.GetMesh(),
diff --git a/app/kumactl/cmd/get/get_traffic_traces_test.go b/app/kumactl/cmd/get/get_traffic_traces_test.go
index 827055306..bfcead435 100644
--- a/app/kumactl/cmd/get/get_traffic_traces_test.go
+++ b/app/kumactl/cmd/get/get_traffic_traces_test.go
@@ -16,14 +16,12 @@ import (
 
 	"github.com/kumahq/kuma/api/mesh/v1alpha1"
 	"github.com/kumahq/kuma/app/kumactl/cmd"
-	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
-	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
 	"github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
 	memory_resources "github.com/kumahq/kuma/pkg/plugins/resources/memory"
+	test_kumactl "github.com/kumahq/kuma/pkg/test/kumactl"
 	test_model "github.com/kumahq/kuma/pkg/test/resources/model"
-	"github.com/kumahq/kuma/pkg/util/test"
 )
 
 var _ = Describe("kumactl get traffic-traces", func() {
@@ -71,25 +69,17 @@ var _ = Describe("kumactl get traffic-traces", func() {
 
 	Describe("GetTrafficTraceCmd", func() {
 
-		var rootCtx *kumactl_cmd.RootContext
 		var rootCmd *cobra.Command
 		var buf *bytes.Buffer
 		var store core_store.ResourceStore
 		rootTime, _ := time.Parse(time.RFC3339, "2008-04-27T16:05:36.995Z")
 		BeforeEach(func() {
 			// setup
-			rootCtx = &kumactl_cmd.RootContext{
-				Runtime: kumactl_cmd.RootRuntime{
-					Now: func() time.Time { return rootTime },
-					NewResourceStore: func(*config_proto.ControlPlaneCoordinates_ApiServer) (core_store.ResourceStore, error) {
-						return store, nil
-					},
-					NewAPIServerClient: test.GetMockNewAPIServerClient(),
-				},
-			}
-
 			store = core_store.NewPaginationStore(memory_resources.NewStore())
 
+			rootCtx, err := test_kumactl.MakeRootContext(rootTime, store, mesh.TrafficTraceResourceTypeDescriptor)
+			Expect(err).ToNot(HaveOccurred())
+
 			for _, ds := range trafficTraceResources {
 				err := store.Create(context.Background(), ds, core_store.CreateBy(core_model.MetaToResourceKey(ds.GetMeta())))
 				Expect(err).ToNot(HaveOccurred())
diff --git a/app/kumactl/cmd/get/get_trafficpermissions_test.go b/app/kumactl/cmd/get/get_trafficpermissions_test.go
index e3ffb9cb6..48b4fba21 100644
--- a/app/kumactl/cmd/get/get_trafficpermissions_test.go
+++ b/app/kumactl/cmd/get/get_trafficpermissions_test.go
@@ -16,14 +16,12 @@ import (
 
 	"github.com/kumahq/kuma/api/mesh/v1alpha1"
 	"github.com/kumahq/kuma/app/kumactl/cmd"
-	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
-	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
 	"github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
 	memory_resources "github.com/kumahq/kuma/pkg/plugins/resources/memory"
+	test_kumactl "github.com/kumahq/kuma/pkg/test/kumactl"
 	test_model "github.com/kumahq/kuma/pkg/test/resources/model"
-	"github.com/kumahq/kuma/pkg/util/test"
 )
 
 var _ = Describe("kumactl get traffic-permissions", func() {
@@ -81,25 +79,17 @@ var _ = Describe("kumactl get traffic-permissions", func() {
 
 	Describe("GetTrafficPermissionsCmd", func() {
 
-		var rootCtx *kumactl_cmd.RootContext
 		var rootCmd *cobra.Command
 		var buf *bytes.Buffer
 		var store core_store.ResourceStore
 		rootTime, _ := time.Parse(time.RFC3339, "2008-04-27T16:05:36.995Z")
 		BeforeEach(func() {
 			// setup
-			rootCtx = &kumactl_cmd.RootContext{
-				Runtime: kumactl_cmd.RootRuntime{
-					Now: func() time.Time { return rootTime },
-					NewResourceStore: func(*config_proto.ControlPlaneCoordinates_ApiServer) (core_store.ResourceStore, error) {
-						return store, nil
-					},
-					NewAPIServerClient: test.GetMockNewAPIServerClient(),
-				},
-			}
-
 			store = core_store.NewPaginationStore(memory_resources.NewStore())
 
+			rootCtx, err := test_kumactl.MakeRootContext(rootTime, store, mesh.TrafficPermissionResourceTypeDescriptor)
+			Expect(err).ToNot(HaveOccurred())
+
 			for _, ds := range trafficPermissionResources {
 				err := store.Create(context.Background(), ds, core_store.CreateBy(core_model.MetaToResourceKey(ds.GetMeta())))
 				Expect(err).ToNot(HaveOccurred())
diff --git a/app/kumactl/cmd/get/get_zone-ingresses_test.go b/app/kumactl/cmd/get/get_zone-ingresses_test.go
index 742a835b0..e34a2516e 100644
--- a/app/kumactl/cmd/get/get_zone-ingresses_test.go
+++ b/app/kumactl/cmd/get/get_zone-ingresses_test.go
@@ -16,14 +16,12 @@ import (
 
 	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
 	"github.com/kumahq/kuma/app/kumactl/cmd"
-	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
-	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
 	core_mesh "github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
 	memory_resources "github.com/kumahq/kuma/pkg/plugins/resources/memory"
+	test_kumactl "github.com/kumahq/kuma/pkg/test/kumactl"
 	test_model "github.com/kumahq/kuma/pkg/test/resources/model"
-	"github.com/kumahq/kuma/pkg/util/test"
 )
 
 var _ = Describe("kumactl get zone-ingresses", func() {
@@ -70,25 +68,17 @@ var _ = Describe("kumactl get zone-ingresses", func() {
 
 	Describe("GetZoneIngressCmd", func() {
 
-		var rootCtx *kumactl_cmd.RootContext
 		var rootCmd *cobra.Command
 		var buf *bytes.Buffer
 		var store core_store.ResourceStore
 		rootTime, _ := time.Parse(time.RFC3339, "2008-04-27T16:05:36.995Z")
 		BeforeEach(func() {
 			// setup
-			rootCtx = &kumactl_cmd.RootContext{
-				Runtime: kumactl_cmd.RootRuntime{
-					Now: func() time.Time { return rootTime },
-					NewResourceStore: func(*config_proto.ControlPlaneCoordinates_ApiServer) (core_store.ResourceStore, error) {
-						return store, nil
-					},
-					NewAPIServerClient: test.GetMockNewAPIServerClient(),
-				},
-			}
-
 			store = core_store.NewPaginationStore(memory_resources.NewStore())
 
+			rootCtx, err := test_kumactl.MakeRootContext(rootTime, store, core_mesh.ZoneIngressResourceTypeDescriptor)
+			Expect(err).ToNot(HaveOccurred())
+
 			for _, cb := range zoneIngresses {
 				err := store.Create(context.Background(), cb, core_store.CreateBy(core_model.MetaToResourceKey(cb.GetMeta())))
 				Expect(err).ToNot(HaveOccurred())
diff --git a/app/kumactl/cmd/get/get_zones_test.go b/app/kumactl/cmd/get/get_zones_test.go
index 2c4bf5de8..a99a1b380 100644
--- a/app/kumactl/cmd/get/get_zones_test.go
+++ b/app/kumactl/cmd/get/get_zones_test.go
@@ -16,14 +16,12 @@ import (
 
 	"github.com/kumahq/kuma/api/system/v1alpha1"
 	"github.com/kumahq/kuma/app/kumactl/cmd"
-	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
-	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
 	"github.com/kumahq/kuma/pkg/core/resources/apis/system"
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
 	memory_resources "github.com/kumahq/kuma/pkg/plugins/resources/memory"
+	test_kumactl "github.com/kumahq/kuma/pkg/test/kumactl"
 	test_model "github.com/kumahq/kuma/pkg/test/resources/model"
-	"github.com/kumahq/kuma/pkg/util/test"
 )
 
 var _ = Describe("kumactl get zones", func() {
@@ -45,25 +43,16 @@ var _ = Describe("kumactl get zones", func() {
 
 	Describe("GetZoneCmd", func() {
 
-		var rootCtx *kumactl_cmd.RootContext
 		var rootCmd *cobra.Command
 		var buf *bytes.Buffer
 		var store core_store.ResourceStore
 		rootTime, _ := time.Parse(time.RFC3339, "2008-04-27T16:05:36.995Z")
 		BeforeEach(func() {
 			// setup
-			rootCtx = &kumactl_cmd.RootContext{
-				Runtime: kumactl_cmd.RootRuntime{
-					Now: func() time.Time { return rootTime },
-					NewResourceStore: func(*config_proto.ControlPlaneCoordinates_ApiServer) (core_store.ResourceStore, error) {
-						return store, nil
-					},
-					NewAPIServerClient: test.GetMockNewAPIServerClient(),
-				},
-			}
-
 			store = core_store.NewPaginationStore(memory_resources.NewStore())
 
+			rootCtx, err := test_kumactl.MakeRootContext(rootTime, store, system.ZoneResourceTypeDescriptor)
+			Expect(err).ToNot(HaveOccurred())
 			for _, cb := range zoneResources {
 				err := store.Create(context.Background(), cb, core_store.CreateBy(core_model.MetaToResourceKey(cb.GetMeta())))
 				Expect(err).ToNot(HaveOccurred())
diff --git a/app/kumactl/cmd/inspect/inspect_dataplanes_test.go b/app/kumactl/cmd/inspect/inspect_dataplanes_test.go
index f15f80054..892a8df31 100644
--- a/app/kumactl/cmd/inspect/inspect_dataplanes_test.go
+++ b/app/kumactl/cmd/inspect/inspect_dataplanes_test.go
@@ -17,14 +17,13 @@ import (
 
 	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
 	"github.com/kumahq/kuma/app/kumactl/cmd"
-	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
 	"github.com/kumahq/kuma/app/kumactl/pkg/resources"
 	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
 	core_mesh "github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	"github.com/kumahq/kuma/pkg/core/resources/model"
+	test_kumactl "github.com/kumahq/kuma/pkg/test/kumactl"
 	test_model "github.com/kumahq/kuma/pkg/test/resources/model"
 	util_proto "github.com/kumahq/kuma/pkg/util/proto"
-	"github.com/kumahq/kuma/pkg/util/test"
 )
 
 type testDataplaneOverviewClient struct {
@@ -381,7 +380,6 @@ var _ = Describe("kumactl inspect dataplanes", func() {
 
 	Describe("InspectDataplanesCmd", func() {
 
-		var rootCtx *kumactl_cmd.RootContext
 		var rootCmd *cobra.Command
 		var buf *bytes.Buffer
 
@@ -394,14 +392,10 @@ var _ = Describe("kumactl inspect dataplanes", func() {
 				overviews: sampleDataplaneOverview,
 			}
 
-			rootCtx = &kumactl_cmd.RootContext{
-				Runtime: kumactl_cmd.RootRuntime{
-					Now: func() time.Time { return now },
-					NewDataplaneOverviewClient: func(*config_proto.ControlPlaneCoordinates_ApiServer) (resources.DataplaneOverviewClient, error) {
-						return testClient, nil
-					},
-					NewAPIServerClient: test.GetMockNewAPIServerClient(),
-				},
+			rootCtx, err := test_kumactl.MakeRootContext(now, nil)
+			Expect(err).ToNot(HaveOccurred())
+			rootCtx.Runtime.NewDataplaneOverviewClient = func(server *config_proto.ControlPlaneCoordinates_ApiServer) (resources.DataplaneOverviewClient, error) {
+				return testClient, nil
 			}
 
 			rootCmd = cmd.NewRootCmd(rootCtx)
diff --git a/app/kumactl/cmd/inspect/inspect_meshes_test.go b/app/kumactl/cmd/inspect/inspect_meshes_test.go
index 33bab9bf9..b07264a5b 100644
--- a/app/kumactl/cmd/inspect/inspect_meshes_test.go
+++ b/app/kumactl/cmd/inspect/inspect_meshes_test.go
@@ -15,15 +15,13 @@ import (
 
 	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
 	"github.com/kumahq/kuma/app/kumactl/cmd"
-	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
-	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
 	"github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
 	memory_resources "github.com/kumahq/kuma/pkg/plugins/resources/memory"
+	test_kumactl "github.com/kumahq/kuma/pkg/test/kumactl"
 	"github.com/kumahq/kuma/pkg/test/matchers"
 	"github.com/kumahq/kuma/pkg/test/resources/model"
-	"github.com/kumahq/kuma/pkg/util/test"
 )
 
 var _ = Describe("kumactl inspect meshes", func() {
@@ -77,29 +75,21 @@ var _ = Describe("kumactl inspect meshes", func() {
 
 	Describe("InspectMeshesCmd", func() {
 
-		var rootCtx *kumactl_cmd.RootContext
 		var rootCmd *cobra.Command
 		var buf *bytes.Buffer
 		var store core_store.ResourceStore
 		rootTime, _ := time.Parse(time.RFC3339, "2008-04-27T16:05:36.995Z")
 
 		BeforeEach(func() {
-			rootCtx = &kumactl_cmd.RootContext{
-				Runtime: kumactl_cmd.RootRuntime{
-					Now: func() time.Time { return rootTime },
-					NewResourceStore: func(*config_proto.ControlPlaneCoordinates_ApiServer) (core_store.ResourceStore, error) {
-						return store, nil
-					},
-					NewAPIServerClient: test.GetMockNewAPIServerClient(),
-				},
-			}
-
 			store = memory_resources.NewStore()
 			for _, cb := range meshInsightResources {
 				err := store.Create(context.Background(), cb, core_store.CreateBy(core_model.MetaToResourceKey(cb.GetMeta())))
 				Expect(err).ToNot(HaveOccurred())
 			}
 
+			rootCtx, err := test_kumactl.MakeRootContext(rootTime, store)
+			Expect(err).ToNot(HaveOccurred())
+
 			rootCmd = cmd.NewRootCmd(rootCtx)
 			buf = &bytes.Buffer{}
 			rootCmd.SetOut(buf)
diff --git a/app/kumactl/cmd/inspect/inspect_services_test.go b/app/kumactl/cmd/inspect/inspect_services_test.go
index b32282c44..b265d0dce 100644
--- a/app/kumactl/cmd/inspect/inspect_services_test.go
+++ b/app/kumactl/cmd/inspect/inspect_services_test.go
@@ -16,13 +16,12 @@ import (
 
 	"github.com/kumahq/kuma/api/mesh/v1alpha1"
 	"github.com/kumahq/kuma/app/kumactl/cmd"
-	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
 	"github.com/kumahq/kuma/app/kumactl/pkg/resources"
 	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
 	core_mesh "github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
+	test_kumactl "github.com/kumahq/kuma/pkg/test/kumactl"
 	"github.com/kumahq/kuma/pkg/test/resources/model"
-	"github.com/kumahq/kuma/pkg/util/test"
 )
 
 type testServiceOverviewClient struct {
@@ -43,7 +42,6 @@ var _ resources.ServiceOverviewClient = &testServiceOverviewClient{}
 
 var _ = Describe("kumactl inspect services", func() {
 
-	var rootCtx *kumactl_cmd.RootContext
 	var rootCmd *cobra.Command
 	var buf *bytes.Buffer
 	rootTime, _ := time.Parse(time.RFC3339, "2008-04-27T16:05:36.995Z")
@@ -82,17 +80,13 @@ var _ = Describe("kumactl inspect services", func() {
 	}
 
 	BeforeEach(func() {
-		rootCtx = &kumactl_cmd.RootContext{
-			Runtime: kumactl_cmd.RootRuntime{
-				Now: func() time.Time { return rootTime },
-				NewServiceOverviewClient: func(server *config_proto.ControlPlaneCoordinates_ApiServer) (resources.ServiceOverviewClient, error) {
-					return &testServiceOverviewClient{
-						total:     uint32(len(serviceOverviewResources)),
-						overviews: serviceOverviewResources,
-					}, nil
-				},
-				NewAPIServerClient: test.GetMockNewAPIServerClient(),
-			},
+		rootCtx, err := test_kumactl.MakeRootContext(rootTime, nil)
+		Expect(err).ToNot(HaveOccurred())
+		rootCtx.Runtime.NewServiceOverviewClient = func(server *config_proto.ControlPlaneCoordinates_ApiServer) (resources.ServiceOverviewClient, error) {
+			return &testServiceOverviewClient{
+				total:     uint32(len(serviceOverviewResources)),
+				overviews: serviceOverviewResources,
+			}, nil
 		}
 
 		rootCmd = cmd.NewRootCmd(rootCtx)
diff --git a/app/kumactl/cmd/inspect/inspect_zoneingresses_test.go b/app/kumactl/cmd/inspect/inspect_zoneingresses_test.go
index a2927ec87..48f4dbf5c 100644
--- a/app/kumactl/cmd/inspect/inspect_zoneingresses_test.go
+++ b/app/kumactl/cmd/inspect/inspect_zoneingresses_test.go
@@ -15,15 +15,14 @@ import (
 
 	mesh_proto "github.com/kumahq/kuma/api/mesh/v1alpha1"
 	"github.com/kumahq/kuma/app/kumactl/cmd"
-	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
 	"github.com/kumahq/kuma/app/kumactl/pkg/resources"
 	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
 	core_mesh "github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	"github.com/kumahq/kuma/pkg/core/resources/model"
+	test_kumactl "github.com/kumahq/kuma/pkg/test/kumactl"
 	"github.com/kumahq/kuma/pkg/test/matchers"
 	test_model "github.com/kumahq/kuma/pkg/test/resources/model"
 	util_proto "github.com/kumahq/kuma/pkg/util/proto"
-	"github.com/kumahq/kuma/pkg/util/test"
 )
 
 type testZoneIngressOverviewClient struct {
@@ -241,7 +240,6 @@ var _ = Describe("kumactl inspect zone-ingresses", func() {
 
 	Describe("InspectZoneIngressesCmd", func() {
 
-		var rootCtx *kumactl_cmd.RootContext
 		var rootCmd *cobra.Command
 		var buf *bytes.Buffer
 
@@ -254,14 +252,10 @@ var _ = Describe("kumactl inspect zone-ingresses", func() {
 				overviews: sampleZoneIngressOverview,
 			}
 
-			rootCtx = &kumactl_cmd.RootContext{
-				Runtime: kumactl_cmd.RootRuntime{
-					Now: func() time.Time { return now },
-					NewZoneIngressOverviewClient: func(*config_proto.ControlPlaneCoordinates_ApiServer) (resources.ZoneIngressOverviewClient, error) {
-						return testClient, nil
-					},
-					NewAPIServerClient: test.GetMockNewAPIServerClient(),
-				},
+			rootCtx, err := test_kumactl.MakeRootContext(now, nil)
+			Expect(err).ToNot(HaveOccurred())
+			rootCtx.Runtime.NewZoneIngressOverviewClient = func(server *config_proto.ControlPlaneCoordinates_ApiServer) (resources.ZoneIngressOverviewClient, error) {
+				return testClient, nil
 			}
 
 			rootCmd = cmd.NewRootCmd(rootCtx)
diff --git a/app/kumactl/cmd/inspect/inspect_zones_test.go b/app/kumactl/cmd/inspect/inspect_zones_test.go
index 19b97c7ca..2b67f9b10 100644
--- a/app/kumactl/cmd/inspect/inspect_zones_test.go
+++ b/app/kumactl/cmd/inspect/inspect_zones_test.go
@@ -16,15 +16,14 @@ import (
 
 	system_proto "github.com/kumahq/kuma/api/system/v1alpha1"
 	"github.com/kumahq/kuma/app/kumactl/cmd"
-	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
 	"github.com/kumahq/kuma/app/kumactl/pkg/resources"
 	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
 	system_core "github.com/kumahq/kuma/pkg/core/resources/apis/system"
 	"github.com/kumahq/kuma/pkg/core/resources/model"
+	test_kumactl "github.com/kumahq/kuma/pkg/test/kumactl"
 	"github.com/kumahq/kuma/pkg/test/matchers"
 	test_model "github.com/kumahq/kuma/pkg/test/resources/model"
 	util_proto "github.com/kumahq/kuma/pkg/util/proto"
-	"github.com/kumahq/kuma/pkg/util/test"
 )
 
 type testZoneOverviewClient struct {
@@ -215,7 +214,6 @@ var _ = Describe("kumactl inspect zones", func() {
 
 	Describe("InspectZonesCmd", func() {
 
-		var rootCtx *kumactl_cmd.RootContext
 		var rootCmd *cobra.Command
 		var buf *bytes.Buffer
 
@@ -227,15 +225,10 @@ var _ = Describe("kumactl inspect zones", func() {
 				total:     uint32(len(sampleZoneOverview)),
 				overviews: sampleZoneOverview,
 			}
-
-			rootCtx = &kumactl_cmd.RootContext{
-				Runtime: kumactl_cmd.RootRuntime{
-					Now: func() time.Time { return now },
-					NewZoneOverviewClient: func(*config_proto.ControlPlaneCoordinates_ApiServer) (resources.ZoneOverviewClient, error) {
-						return testClient, nil
-					},
-					NewAPIServerClient: test.GetMockNewAPIServerClient(),
-				},
+			rootCtx, err := test_kumactl.MakeRootContext(now, nil)
+			Expect(err).ToNot(HaveOccurred())
+			rootCtx.Runtime.NewZoneOverviewClient = func(server *config_proto.ControlPlaneCoordinates_ApiServer) (resources.ZoneOverviewClient, error) {
+				return testClient, nil
 			}
 
 			rootCmd = cmd.NewRootCmd(rootCtx)
diff --git a/app/kumactl/cmd/root_test.go b/app/kumactl/cmd/root_test.go
index bdec76465..8da62ed3a 100644
--- a/app/kumactl/cmd/root_test.go
+++ b/app/kumactl/cmd/root_test.go
@@ -10,6 +10,7 @@ import (
 	"github.com/kumahq/kuma/app/kumactl/cmd"
 	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
 	"github.com/kumahq/kuma/app/kumactl/pkg/config"
+	"github.com/kumahq/kuma/pkg/core/resources/registry"
 	"github.com/kumahq/kuma/pkg/util/test"
 )
 
@@ -34,6 +35,7 @@ var _ = Describe("kumactl root cmd", func() {
 		// given
 		rootCtx := &kumactl_cmd.RootContext{
 			Runtime: kumactl_cmd.RootRuntime{
+				Registry:           registry.NewTypeRegistry(),
 				NewAPIServerClient: test.GetMockNewAPIServerClient(),
 			},
 		}
@@ -67,6 +69,7 @@ currentContext: local
 		// given
 		rootCtx := &kumactl_cmd.RootContext{
 			Runtime: kumactl_cmd.RootRuntime{
+				Registry:           registry.NewTypeRegistry(),
 				NewAPIServerClient: test.GetMockNewAPIServerClient(),
 			},
 		}
diff --git a/app/kumactl/pkg/resources/store_test.go b/app/kumactl/pkg/resources/store_test.go
index b909046a0..92833d6c9 100644
--- a/app/kumactl/pkg/resources/store_test.go
+++ b/app/kumactl/pkg/resources/store_test.go
@@ -30,7 +30,7 @@ var _ = Describe("Store", func() {
 				Expect(err).ToNot(HaveOccurred())
 
 				// when
-				store, err := NewResourceStore(cp.Coordinates.ApiServer)
+				store, err := NewResourceStore(cp.Coordinates.ApiServer, nil)
 				// then
 				Expect(store).ToNot(BeNil())
 				// and
@@ -51,7 +51,7 @@ var _ = Describe("Store", func() {
 					},
 				}
 				// when
-				store, err := NewResourceStore(cp.Coordinates.ApiServer)
+				store, err := NewResourceStore(cp.Coordinates.ApiServer, nil)
 				// then
 				Expect(store).To(BeNil())
 				// and
diff --git a/pkg/api-server/customization/customization_suite_test.go b/pkg/api-server/customization/customization_suite_test.go
index 67dcf556f..2e71690e9 100644
--- a/pkg/api-server/customization/customization_suite_test.go
+++ b/pkg/api-server/customization/customization_suite_test.go
@@ -8,10 +8,11 @@ import (
 
 	api_server "github.com/kumahq/kuma/pkg/api-server"
 	"github.com/kumahq/kuma/pkg/api-server/customization"
-	"github.com/kumahq/kuma/pkg/api-server/definitions"
 	config_api_server "github.com/kumahq/kuma/pkg/config/api-server"
 	kuma_cp "github.com/kumahq/kuma/pkg/config/app/kuma-cp"
 	"github.com/kumahq/kuma/pkg/core/resources/manager"
+	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
+	"github.com/kumahq/kuma/pkg/core/resources/registry"
 	"github.com/kumahq/kuma/pkg/core/resources/store"
 	core_metrics "github.com/kumahq/kuma/pkg/metrics"
 	"github.com/kumahq/kuma/pkg/test"
@@ -44,7 +45,7 @@ func createTestApiServer(store store.ResourceStore, config *config_api_server.Ap
 	}
 	cfg := kuma_cp.DefaultConfig()
 	cfg.ApiServer = config
-	apiServer, err := api_server.NewApiServer(resources, wsManager, definitions.All, &cfg, enableGUI, metrics)
+	apiServer, err := api_server.NewApiServer(resources, wsManager, registry.Global().ObjectDescriptors(core_model.HasWsEnabled()), &cfg, enableGUI, metrics)
 	Expect(err).ToNot(HaveOccurred())
 	return apiServer
 }
diff --git a/pkg/api-server/mesh_endpoints_test.go b/pkg/api-server/mesh_endpoints_test.go
index 444c0da1d..a5b26c54a 100644
--- a/pkg/api-server/mesh_endpoints_test.go
+++ b/pkg/api-server/mesh_endpoints_test.go
@@ -230,7 +230,7 @@ var _ = Describe("Resource Endpoints", func() {
 			// and
 			resource := mesh.NewMeshResource()
 			err := resourceStore.Get(context.Background(), resource, store.GetByKey(name, name))
-			Expect(err).To(Equal(store.ErrorResourceNotFound(resource.GetType(), name, name)))
+			Expect(err).To(Equal(store.ErrorResourceNotFound(resource.Descriptor().Name, name, name)))
 		})
 
 		It("should delete non-existing resource", func() {
diff --git a/pkg/api-server/resource_api_client_test.go b/pkg/api-server/resource_api_client_test.go
index 58b333d19..e0cacdaba 100644
--- a/pkg/api-server/resource_api_client_test.go
+++ b/pkg/api-server/resource_api_client_test.go
@@ -10,11 +10,12 @@ import (
 
 	api_server "github.com/kumahq/kuma/pkg/api-server"
 	"github.com/kumahq/kuma/pkg/api-server/customization"
-	"github.com/kumahq/kuma/pkg/api-server/definitions"
 	config_api_server "github.com/kumahq/kuma/pkg/config/api-server"
 	kuma_cp "github.com/kumahq/kuma/pkg/config/app/kuma-cp"
 	"github.com/kumahq/kuma/pkg/core/resources/manager"
+	"github.com/kumahq/kuma/pkg/core/resources/model"
 	"github.com/kumahq/kuma/pkg/core/resources/model/rest"
+	"github.com/kumahq/kuma/pkg/core/resources/registry"
 	"github.com/kumahq/kuma/pkg/core/resources/store"
 	core_metrics "github.com/kumahq/kuma/pkg/metrics"
 	"github.com/kumahq/kuma/pkg/test"
@@ -115,7 +116,7 @@ func createTestApiServer(store store.ResourceStore, config *config_api_server.Ap
 		config.Auth.ClientCertsDir = filepath.Join("..", "..", "test", "certs", "client")
 	}
 
-	defs := append(definitions.All, SampleTrafficRouteWsDefinition)
+	defs := append(registry.Global().ObjectDescriptors(model.HasWsEnabled()), sample_model.TrafficRouteResourceTypeDescriptor)
 	resources := manager.NewResourceManager(store)
 	wsManager := customization.NewAPIList()
 	cfg := kuma_cp.DefaultConfig()
diff --git a/pkg/api-server/resource_endpoints_test.go b/pkg/api-server/resource_endpoints_test.go
index eaaab9e0f..c16e235c8 100644
--- a/pkg/api-server/resource_endpoints_test.go
+++ b/pkg/api-server/resource_endpoints_test.go
@@ -616,7 +616,7 @@ var _ = Describe("Resource Endpoints", func() {
 			// and
 			resource := sample_model.NewTrafficRouteResource()
 			err := resourceStore.Get(context.Background(), resource, store.GetByKey(name, mesh))
-			Expect(err).To(Equal(store.ErrorResourceNotFound(resource.GetType(), name, mesh)))
+			Expect(err).To(Equal(store.ErrorResourceNotFound(resource.Descriptor().Name, name, mesh)))
 		})
 
 		It("should delete non-existing resource", func() {
diff --git a/pkg/api-server/sample_resource_definition_test.go b/pkg/api-server/sample_resource_definition_test.go
deleted file mode 100644
index 8645d1fbe..000000000
--- a/pkg/api-server/sample_resource_definition_test.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package api_server_test
-
-import (
-	"github.com/kumahq/kuma/pkg/api-server/definitions"
-	sample_model "github.com/kumahq/kuma/pkg/test/resources/apis/sample"
-)
-
-var SampleTrafficRouteWsDefinition = definitions.ResourceWsDefinition{
-	Type:     sample_model.TrafficRouteType,
-	Path:     "sample-traffic-routes",
-	ReadOnly: false,
-}
diff --git a/pkg/hds/authn/callbacks_test.go b/pkg/hds/authn/callbacks_test.go
index 0c88ef428..1b52c06e4 100644
--- a/pkg/hds/authn/callbacks_test.go
+++ b/pkg/hds/authn/callbacks_test.go
@@ -36,7 +36,7 @@ func (t *testAuthenticator) Authenticate(_ context.Context, resource model.Resou
 			return nil
 		}
 	default:
-		return errors.Errorf("no matching authenticator for %s resource", resource.GetType())
+		return errors.Errorf("no matching authenticator for %s resource", resource.Descriptor().Name)
 	}
 
 	return errors.New("invalid credential")
diff --git a/pkg/insights/resyncer_test.go b/pkg/insights/resyncer_test.go
index c94b03643..265df3177 100644
--- a/pkg/insights/resyncer_test.go
+++ b/pkg/insights/resyncer_test.go
@@ -15,6 +15,7 @@ import (
 	core_mesh "github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	"github.com/kumahq/kuma/pkg/core/resources/manager"
 	"github.com/kumahq/kuma/pkg/core/resources/model"
+	"github.com/kumahq/kuma/pkg/core/resources/registry"
 	"github.com/kumahq/kuma/pkg/core/resources/store"
 	"github.com/kumahq/kuma/pkg/events"
 	"github.com/kumahq/kuma/pkg/insights"
@@ -67,6 +68,7 @@ var _ = Describe("Insight Persistence", func() {
 				Expect(d).To(Equal(55 * time.Second)) // should be equal MaxResyncTimeout - MinResyncTimeout
 				return tickCh
 			},
+			Registry: registry.Global(),
 		})
 		go func(stopCh chan struct{}) {
 			err := resyncer.Start(stopCh)
diff --git a/pkg/kds/global/components_test.go b/pkg/kds/global/components_test.go
index 8ef2dfd3a..720610b13 100644
--- a/pkg/kds/global/components_test.go
+++ b/pkg/kds/global/components_test.go
@@ -17,7 +17,6 @@ import (
 	"github.com/kumahq/kuma/pkg/core/resources/model"
 	"github.com/kumahq/kuma/pkg/core/resources/registry"
 	"github.com/kumahq/kuma/pkg/core/resources/store"
-	"github.com/kumahq/kuma/pkg/kds/definitions"
 	"github.com/kumahq/kuma/pkg/kds/global"
 	"github.com/kumahq/kuma/pkg/kds/reconcile"
 	sync_store "github.com/kumahq/kuma/pkg/kds/store"
@@ -45,7 +44,7 @@ var _ = Describe("Global Sync", func() {
 		for i := 0; i < numOfZones; i++ {
 			wg.Add(1)
 			zoneStore := memory.NewStore()
-			serverStream := kds_setup.StartServer(zoneStore, wg, fmt.Sprintf(zoneName, i), definitions.All.Get(), reconcile.Any)
+			serverStream := kds_setup.StartServer(zoneStore, wg, fmt.Sprintf(zoneName, i), registry.Global().ObjectTypes(model.HasKdsEnabled()), reconcile.Any)
 			serverStreams = append(serverStreams, serverStream)
 			zoneStores = append(zoneStores, zoneStore)
 		}
@@ -176,14 +175,9 @@ var _ = Describe("Global Sync", func() {
 		}
 
 		// take all mesh-scoped types and exclude types that won't be synced
-		actualProvidedTypes := []model.ResourceType{}
-		for _, typ := range registry.Global().ListTypes() {
-			obj, err := registry.Global().NewObject(typ)
-			Expect(err).ToNot(HaveOccurred())
-			if obj.Scope() == model.ScopeMesh && !excludeTypes[typ] {
-				actualProvidedTypes = append(actualProvidedTypes, typ)
-			}
-		}
+		actualProvidedTypes := registry.Global().ObjectTypes(model.HasScope(model.ScopeMesh), model.TypeFilterFn(func(descriptor model.ResourceTypeDescriptor) bool {
+			return !excludeTypes[descriptor.Name]
+		}))
 
 		// plus 4 global-scope types
 		extraTypes := []model.ResourceType{
@@ -194,7 +188,7 @@ var _ = Describe("Global Sync", func() {
 		}
 
 		actualProvidedTypes = append(actualProvidedTypes, extraTypes...)
-		Expect(actualProvidedTypes).To(ConsistOf(definitions.All.Select(definitions.ProvidedByGlobal)))
+		Expect(actualProvidedTypes).To(ConsistOf(registry.Global().ObjectTypes(model.HasKDSFlag(model.ProvidedByGlobal))))
 	})
 
 })
diff --git a/pkg/kds/server/server_test.go b/pkg/kds/server/server_test.go
index 9b08bdc66..2cc707e43 100644
--- a/pkg/kds/server/server_test.go
+++ b/pkg/kds/server/server_test.go
@@ -2,6 +2,7 @@ package server_test
 
 import (
 	"context"
+	"reflect"
 	"sync"
 	"time"
 
@@ -14,8 +15,8 @@ import (
 	"github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
 	"github.com/kumahq/kuma/pkg/core/resources/apis/system"
 	"github.com/kumahq/kuma/pkg/core/resources/model"
+	"github.com/kumahq/kuma/pkg/core/resources/registry"
 	"github.com/kumahq/kuma/pkg/core/resources/store"
-	"github.com/kumahq/kuma/pkg/kds/definitions"
 	"github.com/kumahq/kuma/pkg/kds/reconcile"
 	"github.com/kumahq/kuma/pkg/plugins/resources/memory"
 	kds_samples "github.com/kumahq/kuma/pkg/test/kds/samples"
@@ -43,7 +44,7 @@ var _ = Describe("KDS Server", func() {
 
 		wg := &sync.WaitGroup{}
 		wg.Add(1)
-		stream := kds_setup.StartServer(s, wg, "test-cluster", definitions.All.Get(), reconcile.Any)
+		stream := kds_setup.StartServer(s, wg, "test-cluster", registry.Global().ObjectTypes(model.HasKdsEnabled()), reconcile.Any)
 
 		tc = &kds_verifier.TestContextImpl{
 			ResourceStore:      s,
@@ -81,7 +82,13 @@ var _ = Describe("KDS Server", func() {
 			kds_samples.Config,
 			kds_samples.Gateway,
 		}).
-			To(HaveLen(len(definitions.All)))
+			To(WithTransform(func(messages []proto.Message) []string {
+				var res []string
+				for _, m := range messages {
+					res = append(res, reflect.TypeOf(m).String())
+				}
+				return res
+			}, HaveLen(len(registry.Global().ObjectTypes(model.HasKdsEnabled())))))
 
 		vrf := kds_verifier.New().
 			// NOTE: The resources all have to be created before any DiscoveryRequests are made.
diff --git a/pkg/kds/zone/components_test.go b/pkg/kds/zone/components_test.go
index de0db8406..2b8ed1a3d 100644
--- a/pkg/kds/zone/components_test.go
+++ b/pkg/kds/zone/components_test.go
@@ -22,7 +22,6 @@ import (
 	"github.com/kumahq/kuma/pkg/core/runtime/component"
 	kds_client "github.com/kumahq/kuma/pkg/kds/client"
 	kds_context "github.com/kumahq/kuma/pkg/kds/context"
-	"github.com/kumahq/kuma/pkg/kds/definitions"
 	sync_store "github.com/kumahq/kuma/pkg/kds/store"
 	"github.com/kumahq/kuma/pkg/kds/zone"
 	"github.com/kumahq/kuma/pkg/plugins/resources/memory"
@@ -46,7 +45,7 @@ var _ = Describe("Zone Sync", func() {
 	zoneName := "zone-1"
 
 	newPolicySink := func(zoneName string, resourceSyncer sync_store.ResourceSyncer, cs *grpc.MockClientStream, rt core_runtime.Runtime) component.Component {
-		return kds_client.NewKDSSink(core.Log, definitions.All.Select(definitions.ConsumedByZone), kds_client.NewKDSStream(cs, zoneName), zone.Callbacks(rt, resourceSyncer, false, zoneName, nil))
+		return kds_client.NewKDSSink(core.Log, registry.Global().ObjectTypes(model.HasKDSFlag(model.ConsumedByZone)), kds_client.NewKDSStream(cs, zoneName), zone.Callbacks(rt, resourceSyncer, false, zoneName, nil))
 	}
 	start := func(comp component.Component, stop chan struct{}) {
 		go func() {
@@ -87,7 +86,7 @@ var _ = Describe("Zone Sync", func() {
 		wg.Add(1)
 
 		kdsCtx := kds_context.DefaultContext(manager.NewResourceManager(globalStore), "global")
-		serverStream := setup.StartServer(globalStore, wg, "global", definitions.All.Select(definitions.ConsumedByZone), kdsCtx.GlobalProvidedFilter)
+		serverStream := setup.StartServer(globalStore, wg, "global", registry.Global().ObjectTypes(model.HasKDSFlag(model.ConsumedByZone)), kdsCtx.GlobalProvidedFilter)
 
 		stop := make(chan struct{})
 		clientStream := serverStream.ClientStream(stop)
@@ -154,14 +153,9 @@ var _ = Describe("Zone Sync", func() {
 		}
 
 		// take all mesh-scoped types and exclude types that won't be synced
-		actualConsumedTypes := []model.ResourceType{}
-		for _, typ := range registry.Global().ListTypes() {
-			obj, err := registry.Global().NewObject(typ)
-			Expect(err).ToNot(HaveOccurred())
-			if obj.Scope() == model.ScopeMesh && !excludeTypes[typ] {
-				actualConsumedTypes = append(actualConsumedTypes, typ)
-			}
-		}
+		actualConsumedTypes := registry.Global().ObjectTypes(model.HasScope(model.ScopeMesh), model.TypeFilterFn(func(descriptor model.ResourceTypeDescriptor) bool {
+			return !excludeTypes[descriptor.Name]
+		}))
 
 		// plus 2 global-scope types
 		extraTypes := []model.ResourceType{
@@ -172,7 +166,7 @@ var _ = Describe("Zone Sync", func() {
 		}
 
 		actualConsumedTypes = append(actualConsumedTypes, extraTypes...)
-		Expect(actualConsumedTypes).To(ConsistOf(definitions.All.Select(definitions.ConsumedByZone)))
+		Expect(actualConsumedTypes).To(ConsistOf(registry.Global().ObjectTypes(model.HasKDSFlag(model.ConsumedByZone))))
 	})
 
 	It("should not delete predefined ConfigMaps in the Zone cluster", func() {
diff --git a/pkg/metrics/store/counter_test.go b/pkg/metrics/store/counter_test.go
index 8a9121676..bd0445136 100644
--- a/pkg/metrics/store/counter_test.go
+++ b/pkg/metrics/store/counter_test.go
@@ -14,6 +14,7 @@ import (
 	"github.com/kumahq/kuma/pkg/core/resources/apis/system"
 	"github.com/kumahq/kuma/pkg/core/resources/manager"
 	"github.com/kumahq/kuma/pkg/core/resources/model"
+	"github.com/kumahq/kuma/pkg/core/resources/registry"
 	core_store "github.com/kumahq/kuma/pkg/core/resources/store"
 	"github.com/kumahq/kuma/pkg/events"
 	"github.com/kumahq/kuma/pkg/insights"
@@ -76,6 +77,7 @@ var _ = Describe("Counter", func() {
 				defer tickMtx.RUnlock()
 				return tickCh
 			},
+			Registry: registry.Global(),
 		})
 
 		go func() {
diff --git a/pkg/plugins/resources/k8s/consistent_kind_type_test.go b/pkg/plugins/resources/k8s/consistent_kind_type_test.go
index 3491830df..4e2533577 100644
--- a/pkg/plugins/resources/k8s/consistent_kind_type_test.go
+++ b/pkg/plugins/resources/k8s/consistent_kind_type_test.go
@@ -35,7 +35,7 @@ var _ = Describe("Consistent Kind Types", func() {
 			obj, err := k8sTypes.NewObject(res.GetSpec())
 			Expect(err).ToNot(HaveOccurred())
 
-			Expect(obj.GetObjectKind().GroupVersionKind().Kind).To(Equal(string(res.GetType())))
+			Expect(obj.GetObjectKind().GroupVersionKind().Kind).To(Equal(string(res.Descriptor().Name)))
 		}
 	})
 
@@ -43,17 +43,17 @@ var _ = Describe("Consistent Kind Types", func() {
 		types := core_registry.Global()
 		k8sTypes := k8s_registry.Global()
 
-		for _, typ := range types.ListTypes() {
-			if IgnoredTypes[typ] {
+		for _, desc := range types.ObjectDescriptors() {
+			if IgnoredTypes[desc.Name] {
 				continue
 			}
 
-			res, err := types.NewObject(typ)
+			res, err := types.NewObject(desc.Name)
 			Expect(err).ToNot(HaveOccurred())
 			obj, err := k8sTypes.NewObject(res.GetSpec())
 			Expect(err).ToNot(HaveOccurred())
 
-			Expect(obj.GetObjectKind().GroupVersionKind().Kind).To(Equal(string(res.GetType())))
+			Expect(obj.GetObjectKind().GroupVersionKind().Kind).To(Equal(string(res.Descriptor().Name)))
 		}
 	})
 })
diff --git a/pkg/plugins/resources/k8s/store_test.go b/pkg/plugins/resources/k8s/store_test.go
index 03af8d304..f9a46a0e3 100644
--- a/pkg/plugins/resources/k8s/store_test.go
+++ b/pkg/plugins/resources/k8s/store_test.go
@@ -526,7 +526,7 @@ var _ = Describe("KubernetesStore", func() {
 
 			// then
 			Expect(err).To(HaveOccurred())
-			Expect(err).To(Equal(store.ErrorResourceNotFound(resource.GetType(), coreName, mesh)))
+			Expect(err).To(Equal(store.ErrorResourceNotFound(resource.Descriptor().Name, coreName, mesh)))
 		})
 
 		It("should delete an existing resource", func() {
diff --git a/pkg/plugins/resources/remote/store_test.go b/pkg/plugins/resources/remote/store_test.go
index 3446238f4..ab460c416 100644
--- a/pkg/plugins/resources/remote/store_test.go
+++ b/pkg/plugins/resources/remote/store_test.go
@@ -48,8 +48,8 @@ var _ = Describe("RemoteStore", func() {
 		}
 		apis := &core_rest.ApiDescriptor{
 			Resources: map[core_model.ResourceType]core_rest.ResourceApi{
-				sample_core.TrafficRouteType: core_rest.NewResourceApi(sample_core.TrafficRouteType, "traffic-routes"),
-				mesh.MeshType:                core_rest.NewResourceApi(mesh.MeshType, "meshes"),
+				sample_core.TrafficRouteType: core_rest.NewResourceApi(core_model.ScopeMesh, "traffic-routes"),
+				mesh.MeshType:                core_rest.NewResourceApi(core_model.ScopeGlobal, "meshes"),
 			},
 		}
 		return remote.NewStore(client, apis)
@@ -66,8 +66,8 @@ var _ = Describe("RemoteStore", func() {
 		}
 		apis := &core_rest.ApiDescriptor{
 			Resources: map[core_model.ResourceType]core_rest.ResourceApi{
-				sample_core.TrafficRouteType: core_rest.NewResourceApi(sample_core.TrafficRouteType, "traffic-routes"),
-				mesh.MeshType:                core_rest.NewResourceApi(mesh.MeshType, "meshes"),
+				sample_core.TrafficRouteType: core_rest.NewResourceApi(core_model.ScopeMesh, "traffic-routes"),
+				mesh.MeshType:                core_rest.NewResourceApi(core_model.ScopeMesh, "meshes"),
 			},
 		}
 		return remote.NewStore(client, apis)
diff --git a/pkg/plugins/runtime/gateway/suite_test.go b/pkg/plugins/runtime/gateway/suite_test.go
index 25e865da9..b2a7af983 100644
--- a/pkg/plugins/runtime/gateway/suite_test.go
+++ b/pkg/plugins/runtime/gateway/suite_test.go
@@ -58,7 +58,7 @@ func StoreNamedFixture(rt runtime.Runtime, name string) error {
 
 	var opts []store.CreateOptionsFunc
 
-	switch r.Scope() {
+	switch r.Descriptor().Scope {
 	case core_model.ScopeGlobal:
 		opts = append(opts, store.CreateByKey(r.GetMeta().GetName(), ""))
 	case core_model.ScopeMesh:
diff --git a/pkg/test/kumactl/context.go b/pkg/test/kumactl/context.go
new file mode 100644
index 000000000..b1e23cb91
--- /dev/null
+++ b/pkg/test/kumactl/context.go
@@ -0,0 +1,31 @@
+package kumactl
+
+import (
+	"time"
+
+	kumactl_cmd "github.com/kumahq/kuma/app/kumactl/pkg/cmd"
+	config_proto "github.com/kumahq/kuma/pkg/config/app/kumactl/v1alpha1"
+	"github.com/kumahq/kuma/pkg/core/resources/model"
+	"github.com/kumahq/kuma/pkg/core/resources/registry"
+	"github.com/kumahq/kuma/pkg/core/resources/store"
+	util_test "github.com/kumahq/kuma/pkg/util/test"
+)
+
+func MakeRootContext(now time.Time, resourceStore store.ResourceStore, res ...model.ResourceTypeDescriptor) (*kumactl_cmd.RootContext, error) {
+	reg := registry.NewTypeRegistry()
+	for _, r := range res {
+		if err := reg.RegisterType(r); err != nil {
+			return nil, err
+		}
+	}
+	return &kumactl_cmd.RootContext{
+		Runtime: kumactl_cmd.RootRuntime{
+			Registry: reg,
+			Now:      func() time.Time { return now },
+			NewResourceStore: func(server *config_proto.ControlPlaneCoordinates_ApiServer) (store.ResourceStore, error) {
+				return resourceStore, nil
+			},
+			NewAPIServerClient: util_test.GetMockNewAPIServerClient(),
+		},
+	}, nil
+}
diff --git a/pkg/test/resources/apis/sample/sample_types.go b/pkg/test/resources/apis/sample/sample_types.go
index 0d2b991e6..a7b254767 100644
--- a/pkg/test/resources/apis/sample/sample_types.go
+++ b/pkg/test/resources/apis/sample/sample_types.go
@@ -26,10 +26,6 @@ func NewTrafficRouteResource() *TrafficRouteResource {
 	}
 }
 
-func (t *TrafficRouteResource) GetType() model.ResourceType {
-	return TrafficRouteType
-}
-
 func (t *TrafficRouteResource) GetMeta() model.ResourceMeta {
 	return t.Meta
 }
@@ -60,8 +56,8 @@ func (t *TrafficRouteResource) Validate() error {
 	return err.OrNil()
 }
 
-func (t *TrafficRouteResource) Scope() model.ResourceScope {
-	return model.ScopeMesh
+func (t *TrafficRouteResource) Descriptor() model.ResourceTypeDescriptor {
+	return TrafficRouteResourceTypeDescriptor
 }
 
 var _ model.ResourceList = &TrafficRouteResourceList{}
@@ -100,7 +96,17 @@ func (l *TrafficRouteResourceList) GetPagination() *model.Pagination {
 	return &l.Pagination
 }
 
+var TrafficRouteResourceTypeDescriptor model.ResourceTypeDescriptor
+
 func init() {
-	registry.RegisterType(NewTrafficRouteResource())
-	registry.RegistryListType(&TrafficRouteResourceList{})
+	TrafficRouteResourceTypeDescriptor = model.ResourceTypeDescriptor{
+		Name:         TrafficRouteType,
+		Resource:     NewTrafficRouteResource(),
+		ResourceList: &TrafficRouteResourceList{},
+		ReadOnly:     false,
+		AdminOnly:    false,
+		Scope:        model.ScopeMesh,
+		WsPath:       "sample-traffic-routes",
+	}
+	registry.RegisterType(TrafficRouteResourceTypeDescriptor)
 }
diff --git a/pkg/test/store/store_test_template.go b/pkg/test/store/store_test_template.go
index d5633eb7c..68dd929ad 100644
--- a/pkg/test/store/store_test_template.go
+++ b/pkg/test/store/store_test_template.go
@@ -86,7 +86,7 @@ func ExecuteStoreTests(
 			err := s.Create(context.Background(), resource, store.CreateByKey(name, mesh))
 
 			// then
-			Expect(err).To(MatchError(store.ErrorResourceAlreadyExists(resource.GetType(), name, mesh)))
+			Expect(err).To(MatchError(store.ErrorResourceAlreadyExists(resource.Descriptor().Name, name, mesh)))
 		})
 	})
 
@@ -110,7 +110,7 @@ func ExecuteStoreTests(
 			err = s.Update(context.Background(), resource)
 
 			// then
-			Expect(err).To(MatchError(store.ErrorResourceConflict(resource.GetType(), name, mesh)))
+			Expect(err).To(MatchError(store.ErrorResourceConflict(resource.Descriptor().Name, name, mesh)))
 		})
 
 		It("should update an existing resource", func() {
@@ -167,7 +167,7 @@ func ExecuteStoreTests(
 
 			// then
 			Expect(err).To(HaveOccurred())
-			Expect(err).To(Equal(store.ErrorResourceNotFound(resource.GetType(), name, mesh)))
+			Expect(err).To(Equal(store.ErrorResourceNotFound(resource.Descriptor().Name, name, mesh)))
 		})
 
 		It("should not delete resource from another mesh", func() {
@@ -208,7 +208,7 @@ func ExecuteStoreTests(
 			err = s.Get(context.Background(), resource, store.GetByKey(name, mesh))
 
 			// then resource cannot be found
-			Expect(err).To(Equal(store.ErrorResourceNotFound(resource.GetType(), name, mesh)))
+			Expect(err).To(Equal(store.ErrorResourceNotFound(resource.Descriptor().Name, name, mesh)))
 		})
 	})
 
@@ -222,7 +222,7 @@ func ExecuteStoreTests(
 			err := s.Get(context.Background(), resource, store.GetByKey(name, mesh))
 
 			// then
-			Expect(err).To(MatchError(store.ErrorResourceNotFound(resource.GetType(), name, mesh)))
+			Expect(err).To(MatchError(store.ErrorResourceNotFound(resource.Descriptor().Name, name, mesh)))
 		})
 
 		It("should return an error if resource is not found in given mesh", func() {
@@ -236,7 +236,7 @@ func ExecuteStoreTests(
 			err := s.Get(context.Background(), resource, store.GetByKey(name, mesh))
 
 			// then
-			Expect(err).To(Equal(store.ErrorResourceNotFound(resource.GetType(), name, mesh)))
+			Expect(err).To(Equal(store.ErrorResourceNotFound(resource.Descriptor().Name, name, mesh)))
 		})
 
 		It("should return an existing resource", func() {
diff --git a/pkg/xds/auth/callbacks_test.go b/pkg/xds/auth/callbacks_test.go
index 5ae78e164..b31c3e7ec 100644
--- a/pkg/xds/auth/callbacks_test.go
+++ b/pkg/xds/auth/callbacks_test.go
@@ -44,7 +44,7 @@ func (t *testAuthenticator) Authenticate(_ context.Context, resource core_model.
 			return nil
 		}
 	default:
-		return errors.Errorf("no matching authenticator for %s resource", resource.GetType())
+		return errors.Errorf("no matching authenticator for %s resource", resource.Descriptor().Name)
 	}
 
 	return errors.New("invalid credential")