[navi] Update discovery filter (#785)

diff --git a/go.mod b/go.mod
index 43a1999..6ae58bc 100644
--- a/go.mod
+++ b/go.mod
@@ -54,13 +54,14 @@
 	google.golang.org/grpc v1.74.2
 	google.golang.org/protobuf v1.36.7
 	gopkg.in/yaml.v3 v3.0.1
-	helm.sh/helm/v3 v3.18.4
-	istio.io/api v1.26.0-alpha.0.0.20250820113222-47f832b86cdd
-	istio.io/client-go v1.26.0-alpha.0.0.20250820113822-8b883d128bac
-	k8s.io/api v0.33.3
-	k8s.io/apiextensions-apiserver v0.33.3
-	k8s.io/apimachinery v0.33.3
-	k8s.io/client-go v0.33.3
+	helm.sh/helm/v3 v3.18.6
+	istio.io/api v1.26.0-alpha.0.0.20250908200844-f7a34ed800ee
+	istio.io/client-go v1.26.0-alpha.0.0.20250908201345-99e026bfe54f
+	k8s.io/api v0.33.4
+	k8s.io/apiextensions-apiserver v0.33.4
+	k8s.io/apimachinery v0.33.4
+	k8s.io/client-go v0.33.4
+	k8s.io/klog v1.0.0
 	k8s.io/klog/v2 v2.130.1
 	k8s.io/kubectl v0.33.3
 	k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
@@ -119,7 +120,7 @@
 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
 	github.com/dgraph-io/ristretto v0.0.1 // indirect
 	github.com/dimchansky/utfbom v1.1.1 // indirect
-	github.com/dlclark/regexp2 v1.10.0 // indirect
+	github.com/dlclark/regexp2 v1.11.0 // indirect
 	github.com/docker/distribution v2.8.3+incompatible // indirect
 	github.com/docker/go-units v0.5.0 // indirect
 	github.com/dustin/go-humanize v1.0.1 // indirect
@@ -141,6 +142,7 @@
 	github.com/google/btree v1.1.3 // indirect
 	github.com/google/gnostic-models v0.6.9 // indirect
 	github.com/google/go-cmp v0.7.0 // indirect
+	github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a // indirect
 	github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
 	github.com/google/uuid v1.6.0 // indirect
 	github.com/goph/emperror v0.17.2 // indirect
@@ -182,7 +184,8 @@
 	github.com/morikuni/aec v1.0.0 // indirect
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
 	github.com/nikolalohinski/gonja v1.5.3 // indirect
-	github.com/onsi/ginkgo/v2 v2.23.4 // indirect
+	github.com/onsi/ginkgo/v2 v2.24.0 // indirect
+	github.com/onsi/gomega v1.38.0 // indirect
 	github.com/opencontainers/go-digest v1.0.0 // indirect
 	github.com/opencontainers/image-spec v1.1.1 // indirect
 	github.com/opencontainers/runc v1.1.7 // indirect
@@ -194,9 +197,12 @@
 	github.com/pjbgf/sha1cd v0.3.0 // indirect
 	github.com/pkoukk/tiktoken-go v0.1.6 // indirect
 	github.com/planetscale/vtprotobuf v0.6.1-0.20240409071808-615f978279ca // indirect
+	github.com/prometheus/client_golang v1.23.0 // indirect
+	github.com/prometheus/procfs v0.17.0 // indirect
 	github.com/rivo/tview v0.0.0-20220307222120-9994674d60a8 // indirect
 	github.com/rivo/uniseg v0.4.7 // indirect
 	github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 // indirect
+	github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect
 	github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
 	github.com/shopspring/decimal v1.4.0 // indirect
 	github.com/sirupsen/logrus v1.9.3 // indirect
@@ -210,17 +216,15 @@
 	github.com/vbatts/tar-split v0.12.1 // indirect
 	github.com/x448/float16 v0.8.4 // indirect
 	github.com/xanzy/ssh-agent v0.3.3 // indirect
-	github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
-	github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
-	github.com/xeipuuv/gojsonschema v1.2.0 // indirect
 	github.com/xlab/treeprint v1.2.0 // indirect
 	github.com/yargevad/filepathx v1.0.0 // indirect
+	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
+	go.opentelemetry.io/otel v1.37.0 // indirect
+	go.opentelemetry.io/otel/sdk/metric v1.37.0 // indirect
 	go.starlark.net v0.0.0-20230302034142-4b1e35fe2254 // indirect
-	go.uber.org/multierr v1.11.0 // indirect
-	go.uber.org/zap v1.27.0 // indirect
 	go.yaml.in/yaml/v2 v2.4.2 // indirect
 	go.yaml.in/yaml/v3 v3.0.3 // indirect
-	golang.org/x/mod v0.26.0 // indirect
+	golang.org/x/mod v0.27.0 // indirect
 	golang.org/x/oauth2 v0.30.0 // indirect
 	golang.org/x/sync v0.16.0 // indirect
 	golang.org/x/sys v0.35.0 // indirect
@@ -231,7 +235,6 @@
 	gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
-	gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
 	gopkg.in/warnings.v0 v0.1.2 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	k8s.io/cli-runtime v0.33.3 // indirect
diff --git a/go.sum b/go.sum
index 970219c..fbf08c2 100644
--- a/go.sum
+++ b/go.sum
@@ -20,8 +20,8 @@
 cloud.google.com/go/vertexai v0.12.0/go.mod h1:8u+d0TsvBfAAd2x5R6GMgbYhsLgo3J7lmP4bR8g2ig8=
 dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
 dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
-github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
-github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
+github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
+github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
 github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
 github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
 github.com/AssemblyAI/assemblyai-go-sdk v1.3.0 h1:AtOVgGxUycvK4P4ypP+1ZupecvFgnfH+Jsum0o5ILoU=
@@ -215,8 +215,8 @@
 github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
 github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
 github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
-github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
+github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
+github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
 github.com/docker/cli v23.0.1+incompatible h1:LRyWITpGzl2C9e9uGxzisptnxAn1zfZKXy13Ul2Q5oM=
 github.com/docker/cli v23.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
@@ -282,6 +282,7 @@
 github.com/go-git/go-git/v5 v5.13.1 h1:DAQ9APonnlvSWpvolXWIuV6Q6zXy2wHbN4cVlNR5Q+M=
 github.com/go-git/go-git/v5 v5.13.1/go.mod h1:qryJB4cSBoq3FRoBRf5A77joojuBcmPJ0qu3XXXVixc=
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
 github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
 github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
@@ -483,8 +484,8 @@
 github.com/nikolalohinski/gonja v1.5.3/go.mod h1:RmjwxNiXAEqcq1HeK5SSMmqFJvKOfTfXhkJv6YBtPa4=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
-github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
+github.com/onsi/ginkgo/v2 v2.24.0 h1:obZz8LAnHicNdbBqvG3ytAFx8fgza+i1IDpBVcHT2YE=
+github.com/onsi/ginkgo/v2 v2.24.0/go.mod h1:ppTWQ1dh9KM/F1XgpeRqelR+zHVwV81DGRSDnFxK7Sk=
 github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/onsi/gomega v1.38.0 h1:c/WX+w8SLAinvuKKQFh77WEucCnPk4j2OTUr7lt7BeY=
 github.com/onsi/gomega v1.38.0/go.mod h1:OcXcwId0b9QsE7Y49u+BTrL4IdKOBOKnD6VQNTJEB6o=
@@ -542,6 +543,8 @@
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI=
 github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs=
+github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ=
+github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
 github.com/sashabaranov/go-openai v1.40.5 h1:SwIlNdWflzR1Rxd1gv3pUg6pwPc6cQ2uMoHs8ai+/NY=
 github.com/sashabaranov/go-openai v1.40.5/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
 github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
@@ -619,13 +622,6 @@
 github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
 github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
 github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
-github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
-github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
-github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
-github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
-github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
-github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
-github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
 github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
 github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
 github.com/yargevad/filepathx v1.0.0 h1:SYcT+N3tYGi+NvazubCNlvgIPbzAk7i7y2dwg3I5FYc=
@@ -669,10 +665,6 @@
 go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
 go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
 go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
-go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
-go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
-go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
-go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
 go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
 go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
 go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE=
@@ -699,8 +691,8 @@
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
-golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
+golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
+golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -782,8 +774,8 @@
 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
-golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
+golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
+golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -828,8 +820,6 @@
 gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
 gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
-gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
 gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
@@ -845,26 +835,26 @@
 gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
 gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
 gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
-helm.sh/helm/v3 v3.18.4 h1:pNhnHM3nAmDrxz6/UC+hfjDY4yeDATQCka2/87hkZXQ=
-helm.sh/helm/v3 v3.18.4/go.mod h1:WVnwKARAw01iEdjpEkP7Ii1tT1pTPYfM1HsakFKM3LI=
+helm.sh/helm/v3 v3.18.6 h1:S/2CqcYnNfLckkHLI0VgQbxgcDaU3N4A/46E3n9wSNY=
+helm.sh/helm/v3 v3.18.6/go.mod h1:L/dXDR2r539oPlFP1PJqKAC1CUgqHJDLkxKpDGrWnyg=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-istio.io/api v1.26.0-alpha.0.0.20250820113222-47f832b86cdd h1:rXbbklkrc68YSkBExYtJHYNWLNTXP3m9H6EbYgjHtQI=
-istio.io/api v1.26.0-alpha.0.0.20250820113222-47f832b86cdd/go.mod h1:BD3qv/ekm16kvSgvSpuiDawgKhEwG97wx849CednJSg=
-istio.io/client-go v1.26.0-alpha.0.0.20250820113822-8b883d128bac h1:cig/XcnMBOfb88t1qbtnGEbHLffLnv3xQGPDYson1kI=
-istio.io/client-go v1.26.0-alpha.0.0.20250820113822-8b883d128bac/go.mod h1:0XgBG5O4fXCea2bhGlxz9/tcVZvE5tegmn7ST6/hUeQ=
-istio.io/istio v0.0.0-20250824174020-b75eb7fd7d09 h1:bQ8Yd3gJXDLZPjhGln8rG6U0FkDov9T5BNTd6fHYBD0=
-istio.io/istio v0.0.0-20250824174020-b75eb7fd7d09/go.mod h1:/5U2lRJulLgMvq5m2d8pkrGSkboNtW5QT8iG4u2ND78=
-k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8=
-k8s.io/api v0.33.3/go.mod h1:01Y/iLUjNBM3TAvypct7DIj0M0NIZc+PzAHCIo0CYGE=
-k8s.io/apiextensions-apiserver v0.33.3 h1:qmOcAHN6DjfD0v9kxL5udB27SRP6SG/MTopmge3MwEs=
-k8s.io/apiextensions-apiserver v0.33.3/go.mod h1:oROuctgo27mUsyp9+Obahos6CWcMISSAPzQ77CAQGz8=
-k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA=
-k8s.io/apimachinery v0.33.3/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
+istio.io/api v1.26.0-alpha.0.0.20250908200844-f7a34ed800ee h1:pDLJDbgzc69hw3EQHIhO+j/efHketfwARg3eL+QIqss=
+istio.io/api v1.26.0-alpha.0.0.20250908200844-f7a34ed800ee/go.mod h1:BD3qv/ekm16kvSgvSpuiDawgKhEwG97wx849CednJSg=
+istio.io/client-go v1.26.0-alpha.0.0.20250908201345-99e026bfe54f h1:gTRu3JTMDWZ2Qi7RcYIHXK2PPmiCtRAZ47QOiSooCxM=
+istio.io/client-go v1.26.0-alpha.0.0.20250908201345-99e026bfe54f/go.mod h1:k9fittgsfNR2AkUGLYRkEyTbSa1marHI4oqsGkE4AEI=
+k8s.io/api v0.33.4 h1:oTzrFVNPXBjMu0IlpA2eDDIU49jsuEorGHB4cvKupkk=
+k8s.io/api v0.33.4/go.mod h1:VHQZ4cuxQ9sCUMESJV5+Fe8bGnqAARZ08tSTdHWfeAc=
+k8s.io/apiextensions-apiserver v0.33.4 h1:rtq5SeXiDbXmSwxsF0MLe2Mtv3SwprA6wp+5qh/CrOU=
+k8s.io/apiextensions-apiserver v0.33.4/go.mod h1:mWXcZQkQV1GQyxeIjYApuqsn/081hhXPZwZ2URuJeSs=
+k8s.io/apimachinery v0.33.4 h1:SOf/JW33TP0eppJMkIgQ+L6atlDiP/090oaX0y9pd9s=
+k8s.io/apimachinery v0.33.4/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
 k8s.io/cli-runtime v0.33.3 h1:Dgy4vPjNIu8LMJBSvs8W0LcdV0PX/8aGG1DA1W8lklA=
 k8s.io/cli-runtime v0.33.3/go.mod h1:yklhLklD4vLS8HNGgC9wGiuHWze4g7x6XQZ+8edsKEo=
-k8s.io/client-go v0.33.3 h1:M5AfDnKfYmVJif92ngN532gFqakcGi6RvaOF16efrpA=
-k8s.io/client-go v0.33.3/go.mod h1:luqKBQggEf3shbxHY4uVENAxrDISLOarxpTKMiUuujg=
+k8s.io/client-go v0.33.4 h1:TNH+CSu8EmXfitntjUPwaKVPN0AYMbc9F1bBS8/ABpw=
+k8s.io/client-go v0.33.4/go.mod h1:LsA0+hBG2DPwovjd931L/AoaezMPX9CmBgyVyBZmbCY=
+k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
+k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
 k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
 k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
 k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
diff --git a/navigator/pkg/bootstrap/server.go b/navigator/pkg/bootstrap/server.go
index 6b6a5e3..3882b72 100644
--- a/navigator/pkg/bootstrap/server.go
+++ b/navigator/pkg/bootstrap/server.go
@@ -31,12 +31,15 @@
 	"github.com/apache/dubbo-kubernetes/pkg/h2c"
 	dubbokeepalive "github.com/apache/dubbo-kubernetes/pkg/keepalive"
 	kubelib "github.com/apache/dubbo-kubernetes/pkg/kube"
+	"github.com/apache/dubbo-kubernetes/pkg/kube/kclient"
+	"github.com/apache/dubbo-kubernetes/pkg/kube/namespace"
 	"github.com/apache/dubbo-kubernetes/pkg/network"
 	"github.com/fsnotify/fsnotify"
 	"golang.org/x/net/http2"
 	"google.golang.org/grpc"
+	corev1 "k8s.io/api/core/v1"
 	"k8s.io/client-go/rest"
-	"k8s.io/klog/v2"
+	"k8s.io/klog"
 	"net"
 	"net/http"
 	"os"
@@ -93,6 +96,13 @@
 	}
 	s.initMeshConfiguration(args, s.fileWatcher)
 
+	if s.kubeClient != nil {
+		// // Build a namespace watcher. This must have no filter, since this is our input to the filter itself.
+		namespaces := kclient.New[*corev1.Namespace](s.kubeClient)
+		filter := namespace.NewDiscoveryNamespacesFilter(namespaces, s.environment.Watcher, s.internalStop)
+		s.kubeClient = kubelib.SetObjectFilter(s.kubeClient, filter)
+	}
+
 	return s, nil
 }
 
diff --git a/pkg/config/schema/gvr/resource.gen.go b/pkg/config/schema/gvr/resource.gen.go
index 40402ff..1fd1e2e 100644
--- a/pkg/config/schema/gvr/resource.gen.go
+++ b/pkg/config/schema/gvr/resource.gen.go
@@ -41,6 +41,20 @@
 	switch g {
 	case ConfigMap:
 		return false
+	case Namespace:
+		return true
+	case DaemonSet:
+		return false
+	case Deployment:
+		return false
+	case StatefulSet:
+		return false
+	case Secret:
+		return false
+	case Service:
+		return false
+	case ServiceAccount:
+		return false
 	}
 	return false
 }
diff --git a/pkg/config/schema/kubetypes/resources.gen.go b/pkg/config/schema/kubetypes/resources.gen.go
index 9e48eac..0eb976b 100644
--- a/pkg/config/schema/kubetypes/resources.gen.go
+++ b/pkg/config/schema/kubetypes/resources.gen.go
@@ -30,8 +30,9 @@
 		return gvk.ConfigMap, true
 	case *istioioapimeshv1alpha1.MeshConfig:
 		return gvk.MeshConfig, true
+	case *k8sioapicorev1.Namespace:
+		return gvk.Namespace, true
 	default:
 		return config.GroupVersionKind{}, false
 	}
-
 }
diff --git a/pkg/kube/client.go b/pkg/kube/client.go
index ed208da..638db0f 100644
--- a/pkg/kube/client.go
+++ b/pkg/kube/client.go
@@ -23,8 +23,10 @@
 	"github.com/apache/dubbo-kubernetes/pkg/cluster"
 	"github.com/apache/dubbo-kubernetes/pkg/kube/collections"
 	"github.com/apache/dubbo-kubernetes/pkg/kube/informerfactory"
+	"github.com/apache/dubbo-kubernetes/pkg/kube/kubetypes"
 	"github.com/apache/dubbo-kubernetes/pkg/lazy"
 	"github.com/apache/dubbo-kubernetes/pkg/sleep"
+	"go.uber.org/atomic"
 	istioclient "istio.io/client-go/pkg/clientset/versioned"
 	kubeExtClient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
 	"k8s.io/apimachinery/pkg/api/meta"
@@ -37,25 +39,28 @@
 	"k8s.io/client-go/rest"
 	"k8s.io/client-go/tools/cache"
 	"k8s.io/client-go/tools/clientcmd"
-	"k8s.io/klog/v2"
+	"k8s.io/klog"
 	"net/http"
 	"time"
 )
 
 type client struct {
-	extSet          kubeExtClient.Interface
-	config          *rest.Config
-	revision        string
-	clientFactory   *clientFactory
-	version         lazy.Lazy[*kubeVersion.Info]
-	informerFactory informerfactory.InformerFactory
-	dynamic         dynamic.Interface
-	kube            kubernetes.Interface
-	mapper          meta.ResettableRESTMapper
-	metadata        metadata.Interface
-	http            *http.Client
-	dubbo           istioclient.Interface
-	clusterID       cluster.ID
+	extSet                 kubeExtClient.Interface
+	config                 *rest.Config
+	revision               string
+	clientFactory          *clientFactory
+	version                lazy.Lazy[*kubeVersion.Info]
+	informerFactory        informerfactory.InformerFactory
+	dynamic                dynamic.Interface
+	kube                   kubernetes.Interface
+	mapper                 meta.ResettableRESTMapper
+	metadata               metadata.Interface
+	http                   *http.Client
+	objectFilter           kubetypes.DynamicObjectFilter
+	clusterID              cluster.ID
+	informerWatchesPending *atomic.Int32
+	crdWatcher             kubetypes.CrdWatcher
+	istio                  istioclient.Interface
 }
 
 type Client interface {
@@ -71,7 +76,13 @@
 
 	Informers() informerfactory.InformerFactory
 
+	ObjectFilter() kubetypes.DynamicObjectFilter
+
 	ClusterID() cluster.ID
+
+	CrdWatcher() kubetypes.CrdWatcher
+
+	Shutdown()
 }
 
 type CLIClient interface {
@@ -192,6 +203,14 @@
 	return c.informerFactory
 }
 
+func (c *client) ObjectFilter() kubetypes.DynamicObjectFilter {
+	return c.objectFilter
+}
+
+func (c *client) CrdWatcher() kubetypes.CrdWatcher {
+	return c.crdWatcher
+}
+
 func (c *client) DynamicClientFor(gvk schema.GroupVersionKind, obj *unstructured.Unstructured, namespace string) (dynamic.ResourceInterface, error) {
 	gvr, namespaced := c.bestEffortToGVR(gvk, obj, namespace)
 	var dr dynamic.ResourceInterface
@@ -279,6 +298,11 @@
 	c.informerFactory.Shutdown()
 }
 
+func SetObjectFilter(c Client, filter kubetypes.DynamicObjectFilter) Client {
+	c.(*client).objectFilter = filter
+	return c
+}
+
 func WithCluster(id cluster.ID) ClientOption {
 	return func(c CLIClient) CLIClient {
 		client := c.(*client)
diff --git a/pkg/kube/controllers/common.go b/pkg/kube/controllers/common.go
index a0d4861..b15e8bf 100644
--- a/pkg/kube/controllers/common.go
+++ b/pkg/kube/controllers/common.go
@@ -111,3 +111,7 @@
 	}
 	return o
 }
+
+func ExtractObject(obj any) Object {
+	return Extract[Object](obj)
+}
diff --git a/pkg/kube/kclient/client.go b/pkg/kube/kclient/client.go
index fd14ce5..31df02f 100644
--- a/pkg/kube/kclient/client.go
+++ b/pkg/kube/kclient/client.go
@@ -48,6 +48,12 @@
 	client kube.Client
 }
 
+func New[T controllers.ComparableObject](c kube.Client) Client[T] {
+	return NewFiltered[T](c, Filter{})
+}
+
+type Filter = kubetypes.Filter
+
 func NewFiltered[T controllers.ComparableObject](c kube.Client, filter Filter) Client[T] {
 	gvr := types.MustToGVR[T](types.MustGVKFromType[T]())
 	inf := kubeclient.GetInformerFiltered[T](c, ToOpts(c, gvr, filter), gvr)
@@ -57,8 +63,6 @@
 	}
 }
 
-type Filter = kubetypes.Filter
-
 func ToOpts(c kube.Client, gvr schema.GroupVersionResource, filter Filter) kubetypes.InformerOptions {
 	ns := filter.Namespace
 	if !dubbogvr.IsClusterScoped(gvr) && ns == "" {
@@ -184,6 +188,20 @@
 	return cast
 }
 
+func (n *informerClient[T]) HasSynced() bool {
+	if !n.informer.HasSynced() {
+		return false
+	}
+	n.handlerMu.RLock()
+	defer n.handlerMu.RUnlock()
+	for _, g := range n.registeredHandlers {
+		if !g.registration.HasSynced() {
+			return false
+		}
+	}
+	return true
+}
+
 func (n *informerClient[T]) HasSyncedIgnoringHandlers() bool {
 	return n.informer.HasSynced()
 }
diff --git a/pkg/kube/kclient/interfaces.go b/pkg/kube/kclient/interfaces.go
index 95439df..b50e57c 100644
--- a/pkg/kube/kclient/interfaces.go
+++ b/pkg/kube/kclient/interfaces.go
@@ -52,6 +52,7 @@
 	Start(stop <-chan struct{})
 	ShutdownHandlers()
 	ShutdownHandler(registration cache.ResourceEventHandlerRegistration)
+	HasSynced() bool
 	HasSyncedIgnoringHandlers() bool
 	AddEventHandler(h cache.ResourceEventHandler) cache.ResourceEventHandlerRegistration
 	Index(name string, extract func(o T) []string) RawIndexer
diff --git a/pkg/kube/kubetypes/types.go b/pkg/kube/kubetypes/types.go
index 989de8c..5b6aec0 100644
--- a/pkg/kube/kubetypes/types.go
+++ b/pkg/kube/kubetypes/types.go
@@ -23,6 +23,7 @@
 	"github.com/apache/dubbo-kubernetes/pkg/util/sets"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/apimachinery/pkg/types"
 )
 
@@ -68,3 +69,24 @@
 	// AddHandler registers a handler on namespace, which will be triggered when namespace selected or deselected.
 	AddHandler(func(selected, deselected sets.String))
 }
+
+type staticFilter struct {
+	f func(obj interface{}) bool
+}
+
+var _ DynamicObjectFilter = staticFilter{}
+
+func (s staticFilter) Filter(obj any) bool {
+	return s.f(obj)
+}
+
+func (s staticFilter) AddHandler(func(selected, deselected sets.String)) {
+	// Do nothing
+}
+
+type CrdWatcher interface {
+	HasSynced() bool
+	KnownOrCallback(s schema.GroupVersionResource, f func(stop <-chan struct{})) bool
+	WaitForCRD(s schema.GroupVersionResource, stop <-chan struct{}) bool
+	Run(stop <-chan struct{})
+}
diff --git a/pkg/kube/namespace/filter.go b/pkg/kube/namespace/filter.go
new file mode 100644
index 0000000..b4a2d66
--- /dev/null
+++ b/pkg/kube/namespace/filter.go
@@ -0,0 +1,282 @@
+package namespace
+
+import (
+	"fmt"
+	"k8s.io/klog/v2"
+	"sync"
+
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/labels"
+	"k8s.io/apimachinery/pkg/selection"
+
+	"github.com/apache/dubbo-kubernetes/pkg/config/mesh"
+	"github.com/apache/dubbo-kubernetes/pkg/kube"
+	"github.com/apache/dubbo-kubernetes/pkg/kube/controllers"
+	"github.com/apache/dubbo-kubernetes/pkg/kube/kclient"
+	"github.com/apache/dubbo-kubernetes/pkg/kube/kubetypes"
+	"github.com/apache/dubbo-kubernetes/pkg/util/sets"
+	"github.com/apache/dubbo-kubernetes/pkg/util/slices"
+	meshapi "istio.io/api/mesh/v1alpha1"
+)
+
+type DiscoveryFilter func(obj any) bool
+
+type discoveryNamespacesFilter struct {
+	lock                sync.RWMutex
+	namespaces          kclient.Client[*corev1.Namespace]
+	discoveryNamespaces sets.String
+	discoverySelectors  []labels.Selector // nil if discovery selectors are not specified, permits all namespaces for discovery
+	handlers            []func(added, removed sets.String)
+}
+
+func NewDiscoveryNamespacesFilter(
+	namespaces kclient.Client[*corev1.Namespace],
+	mesh mesh.Watcher,
+	stop <-chan struct{},
+) kubetypes.DynamicObjectFilter {
+	// convert LabelSelectors to Selectors
+	f := &discoveryNamespacesFilter{
+		namespaces:          namespaces,
+		discoveryNamespaces: sets.New[string](),
+	}
+	mesh.AddMeshHandler(func() {
+		f.selectorsChanged(mesh.Mesh().GetDiscoverySelectors(), true)
+	})
+
+	namespaces.AddEventHandler(controllers.EventHandler[*corev1.Namespace]{
+		AddFunc: func(ns *corev1.Namespace) {
+			f.lock.Lock()
+			created := f.namespaceCreatedLocked(ns.ObjectMeta)
+			f.lock.Unlock()
+			// In rare cases, a namespace may be created after objects in the namespace, because there is no synchronization between watches
+			// So we need to notify if we started selecting namespace
+			if created {
+				f.notifyHandlers(sets.New(ns.Name), nil)
+			}
+		},
+		UpdateFunc: func(oldObj, newObj *corev1.Namespace) {
+			f.lock.Lock()
+			membershipChanged, namespaceAdded := f.namespaceUpdatedLocked(oldObj.ObjectMeta, newObj.ObjectMeta)
+			f.lock.Unlock()
+			if membershipChanged {
+				added := sets.New(newObj.Name)
+				var removed sets.String
+				if !namespaceAdded {
+					removed = added
+					added = nil
+				}
+				f.notifyHandlers(added, removed)
+			}
+		},
+		DeleteFunc: func(ns *corev1.Namespace) {
+			f.lock.Lock()
+			defer f.lock.Unlock()
+			// No need to notify handlers for deletes. The namespace was deleted, so the object will be as well (and a delete could not de-select).
+			// Note that specifically for the edge case of a Namespace watcher that is filtering, this will ignore deletes we should
+			// otherwise send.
+			// See kclient.applyDynamicFilter for rationale.
+			f.namespaceDeletedLocked(ns.ObjectMeta)
+		},
+	})
+	// Start namespaces and wait for it to be ready now. This is required for subsequent users, so we want to block
+	namespaces.Start(stop)
+	kube.WaitForCacheSync("discovery filter", stop, namespaces.HasSynced)
+	f.selectorsChanged(mesh.Mesh().GetDiscoverySelectors(), false)
+	return f
+}
+
+func (d *discoveryNamespacesFilter) notifyHandlers(added sets.Set[string], removed sets.String) {
+	// Clone handlers; we handle dynamic handlers so they can change after the filter has started.
+	// Important: handlers are not called under the lock. If they are, then handlers which eventually call discoveryNamespacesFilter.Filter
+	// (as some do in the codebase currently, via kclient.List), will deadlock.
+	d.lock.RLock()
+	handlers := slices.Clone(d.handlers)
+	d.lock.RUnlock()
+	for _, h := range handlers {
+		h(added, removed)
+	}
+}
+
+func (d *discoveryNamespacesFilter) Filter(obj any) bool {
+	// When an object is deleted, obj could be a DeletionFinalStateUnknown marker item.
+	ns, ok := extractObjectNamespace(obj)
+	if !ok {
+		return false
+	}
+	if ns == "" {
+		// Cluster scoped resources. Always included
+		return true
+	}
+
+	d.lock.RLock()
+	defer d.lock.RUnlock()
+	// permit all objects if discovery selectors are not specified
+	if len(d.discoverySelectors) == 0 {
+		return true
+	}
+
+	// permit if object resides in a namespace labeled for discovery
+	return d.discoveryNamespaces.Contains(ns)
+}
+
+func extractObjectNamespace(obj any) (string, bool) {
+	if ns, ok := obj.(string); ok {
+		return ns, true
+	}
+	object := controllers.ExtractObject(obj)
+	if object == nil {
+		// When an object is deleted, obj could be a DeletionFinalStateUnknown marker item.
+		return "", false
+	}
+	if _, ok := object.(*corev1.Namespace); ok {
+		return object.GetName(), true
+	}
+	return object.GetNamespace(), true
+}
+
+func LabelSelectorAsSelector(ps *meshapi.LabelSelector) (labels.Selector, error) {
+	if ps == nil {
+		return labels.Nothing(), nil
+	}
+	if len(ps.MatchLabels)+len(ps.MatchExpressions) == 0 {
+		return labels.Everything(), nil
+	}
+	requirements := make([]labels.Requirement, 0, len(ps.MatchLabels)+len(ps.MatchExpressions))
+	for k, v := range ps.MatchLabels {
+		r, err := labels.NewRequirement(k, selection.Equals, []string{v})
+		if err != nil {
+			return nil, err
+		}
+		requirements = append(requirements, *r)
+	}
+	for _, expr := range ps.MatchExpressions {
+		var op selection.Operator
+		switch metav1.LabelSelectorOperator(expr.Operator) {
+		case metav1.LabelSelectorOpIn:
+			op = selection.In
+		case metav1.LabelSelectorOpNotIn:
+			op = selection.NotIn
+		case metav1.LabelSelectorOpExists:
+			op = selection.Exists
+		case metav1.LabelSelectorOpDoesNotExist:
+			op = selection.DoesNotExist
+		default:
+			return nil, fmt.Errorf("%q is not a valid label selector operator", expr.Operator)
+		}
+		r, err := labels.NewRequirement(expr.Key, op, append([]string(nil), expr.Values...))
+		if err != nil {
+			return nil, err
+		}
+		requirements = append(requirements, *r)
+	}
+	selector := labels.NewSelector()
+	selector = selector.Add(requirements...)
+	return selector, nil
+}
+
+// SelectorsChanged initializes the discovery filter state with the discovery selectors and selected namespaces
+func (d *discoveryNamespacesFilter) selectorsChanged(
+	discoverySelectors []*meshapi.LabelSelector,
+	notify bool,
+) {
+	// Call closure to allow safe defer lock handling
+	selectedNamespaces, deselectedNamespaces := func() (sets.String, sets.String) {
+		d.lock.Lock()
+		defer d.lock.Unlock()
+		var selectors []labels.Selector
+		newDiscoveryNamespaces := sets.New[string]()
+
+		namespaceList := d.namespaces.List("", labels.Everything())
+
+		// convert LabelSelectors to Selectors
+		for _, selector := range discoverySelectors {
+			ls, err := LabelSelectorAsSelector(selector)
+			if err != nil {
+				klog.Errorf("error initializing discovery namespaces filter, invalid discovery selector: %v", err)
+				return nil, nil
+			}
+			selectors = append(selectors, ls)
+		}
+
+		// range over all namespaces to get discovery namespaces
+		for _, ns := range namespaceList {
+			for _, selector := range selectors {
+				if selector.Matches(labels.Set(ns.Labels)) {
+					newDiscoveryNamespaces.Insert(ns.Name)
+				}
+			}
+			// omitting discoverySelectors indicates discovering all namespaces
+			if len(selectors) == 0 {
+				for _, ns := range namespaceList {
+					newDiscoveryNamespaces.Insert(ns.Name)
+				}
+			}
+		}
+
+		// update filter state
+		oldDiscoveryNamespaces := d.discoveryNamespaces
+		d.discoveryNamespaces = newDiscoveryNamespaces
+		d.discoverySelectors = selectors
+		if notify {
+			selectedNamespaces := newDiscoveryNamespaces.Difference(oldDiscoveryNamespaces)
+			deselectedNamespaces := oldDiscoveryNamespaces.Difference(newDiscoveryNamespaces)
+			return selectedNamespaces, deselectedNamespaces
+		}
+		return nil, nil
+	}()
+	if notify {
+		d.notifyHandlers(selectedNamespaces, deselectedNamespaces)
+	}
+}
+
+// namespaceCreated: if newly created namespace is selected, update namespace membership
+func (d *discoveryNamespacesFilter) namespaceCreatedLocked(ns metav1.ObjectMeta) (membershipChanged bool) {
+	if d.isSelectedLocked(ns.Labels) {
+		d.discoveryNamespaces.Insert(ns.Name)
+		// Do not trigger update when there are no selectors. This avoids possibility of double namespace ADDs
+		return len(d.discoverySelectors) != 0
+	}
+	return false
+}
+
+// namespaceUpdatedLocked : if updated namespace was a member and no longer selected, or was not a member and now selected, update namespace membership
+func (d *discoveryNamespacesFilter) namespaceUpdatedLocked(oldNs, newNs metav1.ObjectMeta) (membershipChanged bool, namespaceAdded bool) {
+	if d.discoveryNamespaces.Contains(oldNs.Name) && !d.isSelectedLocked(newNs.Labels) {
+		d.discoveryNamespaces.Delete(oldNs.Name)
+		return true, false
+	}
+	if !d.discoveryNamespaces.Contains(oldNs.Name) && d.isSelectedLocked(newNs.Labels) {
+		d.discoveryNamespaces.Insert(oldNs.Name)
+		return true, true
+	}
+	return false, false
+}
+
+// namespaceDeletedLocked : if deleted namespace was a member, remove it
+func (d *discoveryNamespacesFilter) namespaceDeletedLocked(ns metav1.ObjectMeta) {
+	d.discoveryNamespaces.Delete(ns.Name)
+}
+
+// AddHandler registers a handler on namespace, which will be triggered when namespace selected or deselected.
+// If the namespaces have been synced, trigger the new added handler.
+func (d *discoveryNamespacesFilter) AddHandler(f func(added, removed sets.String)) {
+	d.lock.Lock()
+	defer d.lock.Unlock()
+	d.handlers = append(d.handlers, f)
+}
+
+func (d *discoveryNamespacesFilter) isSelectedLocked(labels labels.Set) bool {
+	// permit all objects if discovery selectors are not specified
+	if len(d.discoverySelectors) == 0 {
+		return true
+	}
+
+	for _, selector := range d.discoverySelectors {
+		if selector.Matches(labels) {
+			return true
+		}
+	}
+
+	return false
+}