diff --git a/Cargo.lock b/Cargo.lock
index 35e3ade81c..79d7a6e679 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3084,11 +3084,14 @@ dependencies = [
  "kaspa-p2p-lib",
  "kaspa-perf-monitor",
  "kaspa-rpc-core",
+ "kaspa-rpc-macros",
  "kaspa-txscript",
  "kaspa-utils",
  "kaspa-utils-tower",
  "kaspa-utxoindex",
  "log",
+ "serde",
+ "thiserror",
  "tokio",
  "triggered",
  "workflow-rpc",
diff --git a/kaspad/src/args.rs b/kaspad/src/args.rs
index 56dd7c1de7..4c21335be0 100644
--- a/kaspad/src/args.rs
+++ b/kaspad/src/args.rs
@@ -5,6 +5,7 @@ use kaspa_consensus_core::{
 };
 use kaspa_core::kaspad_env::version;
 use kaspa_notify::address::tracker::Tracker;
+use kaspa_rpc_core::api::namespaces::Namespaces;
 use kaspa_utils::networking::ContextualNetAddress;
 use kaspa_wrpc_server::address::WrpcNetAddress;
 use serde::Deserialize;
@@ -90,6 +91,8 @@ pub struct Args {
     #[serde(rename = "nogrpc")]
     pub disable_grpc: bool,
     pub ram_scale: f64,
+    #[serde(rename = "rpc-api")]
+    pub rpc_namespaces: Option<Namespaces>,
 }
 
 impl Default for Args {
@@ -140,6 +143,7 @@ impl Default for Args {
             disable_dns_seeding: false,
             disable_grpc: false,
             ram_scale: 1.0,
+            rpc_namespaces: None,
         }
     }
 }
@@ -369,7 +373,15 @@ Setting to 0 prevents the preallocation and sets the maximum to {}, leading to 0
                 .help("Apply a scale factor to memory allocation bounds. Nodes with limited RAM (~4-8GB) should set this to ~0.3-0.5 respectively. Nodes with
 a large RAM (~64GB) can set this value to ~3.0-4.0 and gain superior performance especially for syncing peers faster"),
         )
-        ;
+        .arg(
+            Arg::new("rpc-api")
+                .long("rpc-api")
+                .value_name("namespaces")
+                .require_equals(true)
+                .default_missing_value(None)
+                .value_parser(clap::value_parser!(Namespaces))
+                .help("Specify allowed RPC namespaces exposed over RPC servers."),
+        );
 
     #[cfg(feature = "devnet-prealloc")]
     let cmd = cmd
@@ -448,6 +460,7 @@ impl Args {
             disable_dns_seeding: arg_match_unwrap_or::<bool>(&m, "nodnsseed", defaults.disable_dns_seeding),
             disable_grpc: arg_match_unwrap_or::<bool>(&m, "nogrpc", defaults.disable_grpc),
             ram_scale: arg_match_unwrap_or::<f64>(&m, "ram-scale", defaults.ram_scale),
+            rpc_namespaces: m.get_one::<Namespaces>("rpc-api").cloned().or(defaults.rpc_namespaces),
 
             #[cfg(feature = "devnet-prealloc")]
             num_prealloc_utxos: m.get_one::<u64>("num-prealloc-utxos").cloned(),
@@ -560,4 +573,6 @@ fn arg_match_many_unwrap_or<T: Clone + Send + Sync + 'static>(m: &clap::ArgMatch
       --override-dag-params-file=           Overrides DAG params (allowed only on devnet)
   -s, --service=                            Service command {install, remove, start, stop}
       --nogrpc                              Don't initialize the gRPC server
+      --rpc-api=                            Set available namespaces over RPC server(s).
+                                            (By default all namespaces are enabled)
 */
diff --git a/kaspad/src/daemon.rs b/kaspad/src/daemon.rs
index 0a1e7943ef..a220e95ce7 100644
--- a/kaspad/src/daemon.rs
+++ b/kaspad/src/daemon.rs
@@ -579,6 +579,7 @@ do you confirm? (answer y/n or pass --yes to the Kaspad command line to confirm
         p2p_tower_counters.clone(),
         grpc_tower_counters.clone(),
         system_info,
+        args.rpc_namespaces.clone(),
     ));
     let grpc_service_broadcasters: usize = 3; // TODO: add a command line argument or derive from other arg/config/host-related fields
     let grpc_service = if !args.disable_grpc {
diff --git a/rpc/core/src/api/Extending RpcApi.md b/rpc/core/src/api/Extending RpcApi.md
index 0940d78227..892eee77b9 100644
--- a/rpc/core/src/api/Extending RpcApi.md	
+++ b/rpc/core/src/api/Extending RpcApi.md	
@@ -23,7 +23,7 @@ As an illustration, let's pretend that we add a new `submit_block` method.
    Implement the first as a call to the second.
    (ie. `async fn submit_block(&self, block: RpcBlock, allow_non_daa_blocks: bool) -> RpcResult<SubmitBlockResponse>` and
    `async fn submit_block_call(&self, request: SubmitBlockRequest) -> RpcResult<SubmitBlockResponse>;`)
-6. Implement the function having a `_call` suffix into `kaspa_rpc_core::server::service::RpcCoreService`.
+6. Implement the function having a `_call` suffix into `kaspa_rpc_core::server::service::RpcCoreService` and wrap it with namespace macro with its corresponding category.
 
 ## rpc-grpc
 
diff --git a/rpc/core/src/api/mod.rs b/rpc/core/src/api/mod.rs
index a75056a841..68a2f1145b 100644
--- a/rpc/core/src/api/mod.rs
+++ b/rpc/core/src/api/mod.rs
@@ -4,6 +4,7 @@
 
 pub mod connection;
 pub mod ctl;
+pub mod namespaces;
 pub mod notifications;
 pub mod ops;
 pub mod rpc;
diff --git a/rpc/core/src/api/namespaces.rs b/rpc/core/src/api/namespaces.rs
new file mode 100644
index 0000000000..33e240f6b3
--- /dev/null
+++ b/rpc/core/src/api/namespaces.rs
@@ -0,0 +1,115 @@
+use kaspa_notify::scope::Scope;
+use serde::Deserialize;
+use std::collections::HashSet;
+use std::str::FromStr;
+use thiserror::Error;
+
+/// Enum representing available namespace groups
+#[derive(Debug, Hash, Eq, PartialEq, Clone, Deserialize)]
+pub enum Namespace {
+    General,
+    Networking,
+    DAG,
+    Mining,
+    Wallet,
+    Metrics,
+    Mempool,
+}
+
+#[derive(Debug, Error)]
+pub enum NamespaceError {
+    #[error("Invalid namespace value: {0}")]
+    InvalidValue(String),
+}
+
+impl FromStr for Namespace {
+    type Err = NamespaceError;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s {
+            "General" => Ok(Namespace::General),
+            "Networking" => Ok(Namespace::Networking),
+            "DAG" => Ok(Namespace::DAG),
+            "Mining" => Ok(Namespace::Mining),
+            "Wallet" => Ok(Namespace::Wallet),
+            "Metrics" => Ok(Namespace::Metrics),
+            "Mempool" => Ok(Namespace::Mempool),
+            _ => Err(NamespaceError::InvalidValue(s.to_string())),
+        }
+    }
+}
+
+impl std::fmt::Display for Namespace {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Namespace::General => write!(f, "General"),
+            Namespace::Networking => write!(f, "Networking"),
+            Namespace::DAG => write!(f, "DAG"),
+            Namespace::Mining => write!(f, "Mining"),
+            Namespace::Wallet => write!(f, "Wallet"),
+            Namespace::Metrics => write!(f, "Metrics"),
+            Namespace::Mempool => write!(f, "Mempool"),
+        }
+    }
+}
+
+#[derive(Debug, Clone, Deserialize)]
+pub struct Namespaces {
+    enabled: HashSet<Namespace>,
+}
+
+impl Namespaces {
+    /// Check if a namespace is enabled
+    pub fn is_enabled(&self, namespace: &Namespace) -> bool {
+        self.enabled.contains(namespace)
+    }
+
+    // Determine the namespace associated with a given subscription scope
+    pub fn get_scope_namespace(&self, scope: &Scope) -> Namespace {
+        match scope {
+            Scope::BlockAdded(_) => Namespace::DAG,
+            Scope::VirtualChainChanged(_) => Namespace::DAG,
+            Scope::FinalityConflict(_) => Namespace::DAG,
+            Scope::FinalityConflictResolved(_) => Namespace::DAG,
+            Scope::UtxosChanged(_) => Namespace::Wallet,
+            Scope::SinkBlueScoreChanged(_) => Namespace::DAG,
+            Scope::VirtualDaaScoreChanged(_) => Namespace::DAG,
+            Scope::PruningPointUtxoSetOverride(_) => Namespace::DAG,
+            Scope::NewBlockTemplate(_) => Namespace::Mining,
+        }
+    }
+
+    /// Return enabled namespaces as string for get_info
+    pub fn enabled_namespaces(&self) -> Vec<String> {
+        self.enabled.iter().map(|namespace| namespace.to_string()).collect::<Vec<_>>()
+    }
+}
+
+impl FromStr for Namespaces {
+    type Err = NamespaceError;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        let enabled = s
+            .split(',')
+            .map(str::trim) // To support case like "DAG, Metrics"
+            .map(|name| name.parse::<Namespace>())
+            .collect::<Result<HashSet<_>, _>>()?;
+        Ok(Namespaces { enabled })
+    }
+}
+
+impl Default for Namespaces {
+    fn default() -> Self {
+        Self {
+            enabled: HashSet::from([
+                Namespace::General,
+                Namespace::Networking,
+                Namespace::DAG,
+                Namespace::Mining,
+                Namespace::Wallet,
+                Namespace::Metrics,
+                Namespace::Mempool,
+            ]),
+        }
+    }
+}
diff --git a/rpc/core/src/error.rs b/rpc/core/src/error.rs
index 54763c71ba..fec4cfb6e2 100644
--- a/rpc/core/src/error.rs
+++ b/rpc/core/src/error.rs
@@ -60,6 +60,9 @@ pub enum RpcError {
     #[error("Transaction {0} not found")]
     TransactionNotFound(TransactionId),
 
+    #[error("Method unavailable. {0} namespace is not available.")]
+    UnauthorizedMethod(String),
+
     #[error("Method unavailable. Run the node with the --utxoindex argument.")]
     NoUtxoIndex,
 
diff --git a/rpc/core/src/model/message.rs b/rpc/core/src/model/message.rs
index cb663c394a..b03e1c2167 100644
--- a/rpc/core/src/model/message.rs
+++ b/rpc/core/src/model/message.rs
@@ -269,6 +269,7 @@ pub struct GetInfoResponse {
     pub server_version: String,
     pub is_utxo_indexed: bool,
     pub is_synced: bool,
+    pub namespaces: Vec<String>,
     pub has_notify_command: bool,
     pub has_message_id: bool,
 }
@@ -283,6 +284,7 @@ impl Serializer for GetInfoResponse {
         store!(bool, &self.is_synced, writer)?;
         store!(bool, &self.has_notify_command, writer)?;
         store!(bool, &self.has_message_id, writer)?;
+        store!(Vec<String>, &self.namespaces, writer)?;
 
         Ok(())
     }
@@ -298,8 +300,9 @@ impl Deserializer for GetInfoResponse {
         let is_synced = load!(bool, reader)?;
         let has_notify_command = load!(bool, reader)?;
         let has_message_id = load!(bool, reader)?;
+        let namespaces = load!(Vec<String>, reader)?;
 
-        Ok(Self { p2p_id, mempool_size, server_version, is_utxo_indexed, is_synced, has_notify_command, has_message_id })
+        Ok(Self { p2p_id, mempool_size, server_version, is_utxo_indexed, is_synced, namespaces, has_notify_command, has_message_id })
     }
 }
 
diff --git a/rpc/core/src/model/tests.rs b/rpc/core/src/model/tests.rs
index d931f5ac23..17d40c8318 100644
--- a/rpc/core/src/model/tests.rs
+++ b/rpc/core/src/model/tests.rs
@@ -1,7 +1,7 @@
 #[cfg(test)]
 mod mockery {
 
-    use crate::{model::*, RpcScriptClass};
+    use crate::{api::namespaces::Namespaces, model::*, RpcScriptClass};
     use kaspa_addresses::{Prefix, Version};
     use kaspa_consensus_core::api::BlockCount;
     use kaspa_consensus_core::network::NetworkType;
@@ -486,6 +486,7 @@ mod mockery {
                 server_version: "0.4.2".to_string(),
                 is_utxo_indexed: true,
                 is_synced: false,
+                namespaces: Namespaces::default().enabled_namespaces(),
                 has_notify_command: true,
                 has_message_id: false,
             }
diff --git a/rpc/core/src/wasm/message.rs b/rpc/core/src/wasm/message.rs
index 85c0857023..01bf83dcf4 100644
--- a/rpc/core/src/wasm/message.rs
+++ b/rpc/core/src/wasm/message.rs
@@ -243,6 +243,7 @@ declare! {
         serverVersion : string;
         isUtxoIndexed : boolean;
         isSynced : boolean;
+        namespaces : string[];
         /** GRPC ONLY */
         hasNotifyCommand : boolean;
         /** GRPC ONLY */
diff --git a/rpc/grpc/core/proto/rpc.proto b/rpc/grpc/core/proto/rpc.proto
index 4c36150ad3..0a523a1c94 100644
--- a/rpc/grpc/core/proto/rpc.proto
+++ b/rpc/grpc/core/proto/rpc.proto
@@ -702,6 +702,7 @@ message GetInfoResponseMessage{
   string serverVersion = 3;
   bool isUtxoIndexed = 4;
   bool isSynced = 5;
+  repeated string namespaces = 6;
   bool hasNotifyCommand = 11;
   bool hasMessageId = 12;
   RPCError error = 1000;
diff --git a/rpc/grpc/core/src/convert/message.rs b/rpc/grpc/core/src/convert/message.rs
index c92e824ed0..68ff6a1013 100644
--- a/rpc/grpc/core/src/convert/message.rs
+++ b/rpc/grpc/core/src/convert/message.rs
@@ -183,6 +183,7 @@ from!(item: RpcResult<&kaspa_rpc_core::GetInfoResponse>, protowire::GetInfoRespo
         server_version: item.server_version.clone(),
         is_utxo_indexed: item.is_utxo_indexed,
         is_synced: item.is_synced,
+        namespaces: item.namespaces.clone(),
         has_notify_command: item.has_notify_command,
         has_message_id: item.has_message_id,
         error: None,
@@ -648,6 +649,7 @@ try_from!(item: &protowire::GetInfoResponseMessage, RpcResult<kaspa_rpc_core::Ge
         server_version: item.server_version.clone(),
         is_utxo_indexed: item.is_utxo_indexed,
         is_synced: item.is_synced,
+        namespaces: item.namespaces.clone(),
         has_notify_command: item.has_notify_command,
         has_message_id: item.has_message_id,
     }
diff --git a/rpc/grpc/server/src/tests/rpc_core_mock.rs b/rpc/grpc/server/src/tests/rpc_core_mock.rs
index ada801850e..bdae6295af 100644
--- a/rpc/grpc/server/src/tests/rpc_core_mock.rs
+++ b/rpc/grpc/server/src/tests/rpc_core_mock.rs
@@ -1,3 +1,4 @@
+use api::namespaces::Namespaces;
 use async_channel::{unbounded, Receiver};
 use async_trait::async_trait;
 use kaspa_notify::events::EVENT_TYPE_ARRAY;
@@ -73,6 +74,7 @@ impl RpcApi for RpcCoreMock {
             server_version: "mock".to_string(),
             is_utxo_indexed: false,
             is_synced: false,
+            namespaces: Namespaces::default().enabled_namespaces(),
             has_notify_command: true,
             has_message_id: true,
         })
diff --git a/rpc/macros/src/core/mod.rs b/rpc/macros/src/core/mod.rs
new file mode 100644
index 0000000000..1f278a4d51
--- /dev/null
+++ b/rpc/macros/src/core/mod.rs
@@ -0,0 +1 @@
+pub mod service;
diff --git a/rpc/macros/src/core/service.rs b/rpc/macros/src/core/service.rs
new file mode 100644
index 0000000000..ed34670e67
--- /dev/null
+++ b/rpc/macros/src/core/service.rs
@@ -0,0 +1,19 @@
+use proc_macro::TokenStream;
+use quote::quote;
+use syn::{parse_macro_input, ItemFn, Path};
+
+pub fn namespace(attr: TokenStream, item: TokenStream) -> TokenStream {
+    let api_namespace = parse_macro_input!(attr as Path);
+    let mut func = parse_macro_input!(item as ItemFn);
+
+    let check = syn::parse2(quote! {
+        if !self.namespaces.is_enabled(&#api_namespace) {
+            // As macro processing happens after async_trait processing its wrapped with async_trait return type
+            return std::boxed::Box::pin(std::future::ready(Err(RpcError::UnauthorizedMethod(#api_namespace.to_string()))));
+        }
+    })
+    .unwrap();
+
+    func.block.stmts.insert(0, check);
+    quote!(#func).into()
+}
diff --git a/rpc/macros/src/lib.rs b/rpc/macros/src/lib.rs
index 9ca49bf54a..c5c3ac95ff 100644
--- a/rpc/macros/src/lib.rs
+++ b/rpc/macros/src/lib.rs
@@ -1,5 +1,6 @@
 use proc_macro::TokenStream;
 use proc_macro_error::proc_macro_error;
+mod core;
 mod grpc;
 mod handler;
 mod wrpc;
@@ -45,3 +46,8 @@ pub fn build_grpc_server_interface(input: TokenStream) -> TokenStream {
 pub fn test_wrpc_serializer(input: TokenStream) -> TokenStream {
     wrpc::test::build_test(input)
 }
+
+#[proc_macro_attribute]
+pub fn namespace(attr: TokenStream, item: TokenStream) -> TokenStream {
+    core::service::namespace(attr, item)
+}
diff --git a/rpc/service/Cargo.toml b/rpc/service/Cargo.toml
index 54e9764088..d57e7cae8c 100644
--- a/rpc/service/Cargo.toml
+++ b/rpc/service/Cargo.toml
@@ -24,6 +24,7 @@ kaspa-p2p-flows.workspace = true
 kaspa-p2p-lib.workspace = true
 kaspa-perf-monitor.workspace = true
 kaspa-rpc-core.workspace = true
+kaspa-rpc-macros.workspace = true
 kaspa-txscript.workspace = true
 kaspa-utils.workspace = true
 kaspa-utils-tower.workspace = true
@@ -33,4 +34,6 @@ async-trait.workspace = true
 log.workspace = true
 tokio.workspace = true
 triggered.workspace = true
-workflow-rpc.workspace = true
\ No newline at end of file
+workflow-rpc.workspace = true
+thiserror.workspace = true
+serde.workspace = true
\ No newline at end of file
diff --git a/rpc/service/src/service.rs b/rpc/service/src/service.rs
index c5bae27a68..2012e2ec81 100644
--- a/rpc/service/src/service.rs
+++ b/rpc/service/src/service.rs
@@ -57,6 +57,7 @@ use kaspa_perf_monitor::{counters::CountersSnapshot, Monitor as PerfMonitor};
 use kaspa_rpc_core::{
     api::{
         connection::DynRpcConnection,
+        namespaces::{Namespace, Namespaces},
         ops::{RPC_API_REVISION, RPC_API_VERSION},
         rpc::{RpcApi, MAX_SAFE_WINDOW_SIZE},
     },
@@ -64,6 +65,7 @@ use kaspa_rpc_core::{
     notify::connection::ChannelConnection,
     Notification, RpcError, RpcResult,
 };
+use kaspa_rpc_macros::namespace;
 use kaspa_txscript::{extract_script_pub_key_address, pay_to_address_script};
 use kaspa_utils::expiring_cache::ExpiringCache;
 use kaspa_utils::sysinfo::SystemInfo;
@@ -119,6 +121,7 @@ pub struct RpcCoreService {
     system_info: SystemInfo,
     fee_estimate_cache: ExpiringCache<RpcFeeEstimate>,
     fee_estimate_verbose_cache: ExpiringCache<kaspa_mining::errors::MiningManagerResult<GetFeeEstimateExperimentalResponse>>,
+    namespaces: Namespaces,
 }
 
 const RPC_CORE: &str = "rpc-core";
@@ -144,6 +147,7 @@ impl RpcCoreService {
         p2p_tower_counters: Arc<TowerConnectionCounters>,
         grpc_tower_counters: Arc<TowerConnectionCounters>,
         system_info: SystemInfo,
+        namespaces: Option<Namespaces>,
     ) -> Self {
         // This notifier UTXOs subscription granularity to index-processor or consensus notifier
         let policies = match index_notifier {
@@ -196,7 +200,7 @@ impl RpcCoreService {
         // Protocol converter
         let protocol_converter = Arc::new(ProtocolConverter::new(flow_context.clone()));
 
-        // Create the rcp-core notifier
+        // Create the rpc-core notifier
         let notifier =
             Arc::new(Notifier::new(RPC_CORE, EVENT_TYPE_ARRAY[..].into(), collectors, subscribers, subscription_context, 1, policies));
 
@@ -222,6 +226,7 @@ impl RpcCoreService {
             system_info,
             fee_estimate_cache: ExpiringCache::new(Duration::from_millis(500), Duration::from_millis(1000)),
             fee_estimate_verbose_cache: ExpiringCache::new(Duration::from_millis(500), Duration::from_millis(1000)),
+            namespaces: namespaces.unwrap_or_default(),
         }
     }
 
@@ -289,6 +294,7 @@ impl RpcCoreService {
 
 #[async_trait]
 impl RpcApi for RpcCoreService {
+    #[namespace(Namespace::Mining)]
     async fn submit_block_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -357,6 +363,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         }
     }
 
+    #[namespace(Namespace::Mining)]
     async fn get_block_template_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -393,6 +400,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         })
     }
 
+    #[namespace(Namespace::Mining)]
     async fn get_current_block_color_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -406,6 +414,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         }
     }
 
+    #[namespace(Namespace::DAG)]
     async fn get_block_call(&self, _connection: Option<&DynRpcConnection>, request: GetBlockRequest) -> RpcResult<GetBlockResponse> {
         // TODO: test
         let session = self.consensus_manager.consensus().session().await;
@@ -418,6 +427,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         })
     }
 
+    #[namespace(Namespace::DAG)]
     async fn get_blocks_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -471,6 +481,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Ok(GetBlocksResponse { block_hashes, blocks })
     }
 
+    #[namespace(Namespace::General)]
     async fn get_info_call(&self, _connection: Option<&DynRpcConnection>, _request: GetInfoRequest) -> RpcResult<GetInfoResponse> {
         let is_nearly_synced = self.consensus_manager.consensus().unguarded_session().async_is_nearly_synced().await;
         Ok(GetInfoResponse {
@@ -479,11 +490,13 @@ NOTE: This error usually indicates an RPC conversion error between the node and
             server_version: version().to_string(),
             is_utxo_indexed: self.config.utxoindex,
             is_synced: self.has_sufficient_peer_connectivity() && is_nearly_synced,
+            namespaces: self.namespaces.enabled_namespaces(),
             has_notify_command: true,
             has_message_id: true,
         })
     }
 
+    #[namespace(Namespace::Mempool)]
     async fn get_mempool_entry_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -497,6 +510,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Ok(GetMempoolEntryResponse::new(self.consensus_converter.get_mempool_entry(&session, &transaction)))
     }
 
+    #[namespace(Namespace::Mempool)]
     async fn get_mempool_entries_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -513,6 +527,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Ok(GetMempoolEntriesResponse::new(mempool_entries))
     }
 
+    #[namespace(Namespace::Mempool)]
     async fn get_mempool_entries_by_addresses_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -539,6 +554,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Ok(GetMempoolEntriesByAddressesResponse::new(mempool_entries))
     }
 
+    #[namespace(Namespace::Wallet)]
     async fn submit_transaction_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -564,6 +580,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Ok(SubmitTransactionResponse::new(transaction_id))
     }
 
+    #[namespace(Namespace::Wallet)]
     async fn submit_transaction_replacement_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -581,6 +598,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Ok(SubmitTransactionReplacementResponse::new(transaction_id, (&*replaced_transaction).into()))
     }
 
+    #[namespace(Namespace::General)]
     async fn get_current_network_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -589,6 +607,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Ok(GetCurrentNetworkResponse::new(*self.config.net))
     }
 
+    #[namespace(Namespace::Networking)]
     async fn get_subnetwork_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -597,10 +616,12 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Err(RpcError::NotImplemented)
     }
 
+    #[namespace(Namespace::DAG)]
     async fn get_sink_call(&self, _connection: Option<&DynRpcConnection>, _: GetSinkRequest) -> RpcResult<GetSinkResponse> {
         Ok(GetSinkResponse::new(self.consensus_manager.consensus().unguarded_session().async_get_sink().await))
     }
 
+    #[namespace(Namespace::DAG)]
     async fn get_sink_blue_score_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -610,6 +631,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Ok(GetSinkBlueScoreResponse::new(session.async_get_ghostdag_data(session.async_get_sink().await).await?.blue_score))
     }
 
+    #[namespace(Namespace::DAG)]
     async fn get_virtual_chain_from_block_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -638,6 +660,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Ok(GetVirtualChainFromBlockResponse::new(virtual_chain_batch.removed, virtual_chain_batch.added, accepted_transaction_ids))
     }
 
+    #[namespace(Namespace::DAG)]
     async fn get_block_count_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -646,6 +669,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Ok(self.consensus_manager.consensus().unguarded_session().async_estimate_block_count().await)
     }
 
+    #[namespace(Namespace::Wallet)]
     async fn get_utxos_by_addresses_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -660,6 +684,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Ok(GetUtxosByAddressesResponse::new(self.index_converter.get_utxos_by_addresses_entries(&entry_map)))
     }
 
+    #[namespace(Namespace::Wallet)]
     async fn get_balance_by_address_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -673,6 +698,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Ok(GetBalanceByAddressResponse::new(balance))
     }
 
+    #[namespace(Namespace::Wallet)]
     async fn get_balances_by_addresses_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -694,6 +720,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Ok(GetBalancesByAddressesResponse::new(entries))
     }
 
+    #[namespace(Namespace::DAG)]
     async fn get_coin_supply_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -707,6 +734,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Ok(GetCoinSupplyResponse::new(MAX_SOMPI, circulating_sompi))
     }
 
+    #[namespace(Namespace::Wallet)] // TODO: think again
     async fn get_daa_score_timestamp_estimate_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -774,6 +802,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Ok(GetDaaScoreTimestampEstimateResponse::new(timestamps))
     }
 
+    #[namespace(Namespace::Wallet)]
     async fn get_fee_estimate_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -793,6 +822,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Ok(GetFeeEstimateResponse { estimate })
     }
 
+    #[namespace(Namespace::Wallet)]
     async fn get_fee_estimate_experimental_call(
         &self,
         connection: Option<&DynRpcConnection>,
@@ -817,6 +847,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         }
     }
 
+    #[namespace(Namespace::Wallet)]
     async fn get_utxo_return_address_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -844,10 +875,12 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         }
     }
 
+    #[namespace(Namespace::General)]
     async fn ping_call(&self, _connection: Option<&DynRpcConnection>, _: PingRequest) -> RpcResult<PingResponse> {
         Ok(PingResponse {})
     }
 
+    #[namespace(Namespace::DAG)]
     async fn get_headers_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -856,6 +889,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Err(RpcError::NotImplemented)
     }
 
+    #[namespace(Namespace::DAG)]
     async fn get_block_dag_info_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -878,6 +912,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         ))
     }
 
+    #[namespace(Namespace::DAG)]
     async fn estimate_network_hashes_per_second_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -910,6 +945,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         ))
     }
 
+    #[namespace(Namespace::Networking)]
     async fn add_peer_call(&self, _connection: Option<&DynRpcConnection>, request: AddPeerRequest) -> RpcResult<AddPeerResponse> {
         if !self.config.unsafe_rpc {
             warn!("AddPeer RPC command called while node in safe RPC mode -- ignoring.");
@@ -924,6 +960,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Ok(AddPeerResponse {})
     }
 
+    #[namespace(Namespace::Networking)]
     async fn get_peer_addresses_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -933,6 +970,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Ok(GetPeerAddressesResponse::new(address_manager.get_all_addresses(), address_manager.get_all_banned_addresses()))
     }
 
+    #[namespace(Namespace::Networking)]
     async fn ban_call(&self, _connection: Option<&DynRpcConnection>, request: BanRequest) -> RpcResult<BanResponse> {
         if !self.config.unsafe_rpc {
             warn!("Ban RPC command called while node in safe RPC mode -- ignoring.");
@@ -950,6 +988,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Ok(BanResponse {})
     }
 
+    #[namespace(Namespace::Networking)]
     async fn unban_call(&self, _connection: Option<&DynRpcConnection>, request: UnbanRequest) -> RpcResult<UnbanResponse> {
         if !self.config.unsafe_rpc {
             warn!("Unban RPC command called while node in safe RPC mode -- ignoring.");
@@ -964,6 +1003,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Ok(UnbanResponse {})
     }
 
+    #[namespace(Namespace::Networking)]
     async fn get_connected_peer_info_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -974,6 +1014,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Ok(GetConnectedPeerInfoResponse::new(peer_info))
     }
 
+    #[namespace(Namespace::General)]
     async fn shutdown_call(&self, _connection: Option<&DynRpcConnection>, _: ShutdownRequest) -> RpcResult<ShutdownResponse> {
         if !self.config.unsafe_rpc {
             warn!("Shutdown RPC command called while node in safe RPC mode -- ignoring.");
@@ -995,6 +1036,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Ok(ShutdownResponse {})
     }
 
+    #[namespace(Namespace::DAG)]
     async fn resolve_finality_conflict_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -1007,6 +1049,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Err(RpcError::NotImplemented)
     }
 
+    #[namespace(Namespace::Networking)]
     async fn get_connections_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -1025,6 +1068,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Ok(GetConnectionsResponse { clients, peers, profile_data })
     }
 
+    #[namespace(Namespace::Metrics)]
     async fn get_metrics_call(&self, _connection: Option<&DynRpcConnection>, req: GetMetricsRequest) -> RpcResult<GetMetricsResponse> {
         let CountersSnapshot {
             resident_set_size,
@@ -1118,6 +1162,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Ok(response)
     }
 
+    #[namespace(Namespace::Metrics)]
     async fn get_system_info_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -1136,6 +1181,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         Ok(response)
     }
 
+    #[namespace(Namespace::General)]
     async fn get_server_info_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -1156,6 +1202,7 @@ NOTE: This error usually indicates an RPC conversion error between the node and
         })
     }
 
+    #[namespace(Namespace::DAG)]
     async fn get_sync_status_call(
         &self,
         _connection: Option<&DynRpcConnection>,
@@ -1184,6 +1231,11 @@ NOTE: This error usually indicates an RPC conversion error between the node and
 
     /// Start sending notifications of some type to a listener.
     async fn start_notify(&self, id: ListenerId, scope: Scope) -> RpcResult<()> {
+        let namespace = self.namespaces.get_scope_namespace(&scope);
+        if !self.namespaces.is_enabled(&namespace) {
+            return Err(RpcError::UnauthorizedMethod(namespace.to_string()));
+        }
+
         match scope {
             Scope::UtxosChanged(ref utxos_changed_scope) if !self.config.unsafe_rpc && utxos_changed_scope.addresses.is_empty() => {
                 // The subscription to blanket UtxosChanged notifications is restricted to unsafe mode only
diff --git a/testing/integration/src/rpc_tests.rs b/testing/integration/src/rpc_tests.rs
index 3ca619423a..08bd6fd694 100644
--- a/testing/integration/src/rpc_tests.rs
+++ b/testing/integration/src/rpc_tests.rs
@@ -15,7 +15,14 @@ use kaspa_notify::{
         SinkBlueScoreChangedScope, UtxosChangedScope, VirtualChainChangedScope, VirtualDaaScoreChangedScope,
     },
 };
-use kaspa_rpc_core::{api::rpc::RpcApi, model::*, Notification};
+use kaspa_rpc_core::{
+    api::{
+        namespaces::{Namespace, Namespaces},
+        rpc::RpcApi,
+    },
+    model::*,
+    Notification,
+};
 use kaspa_utils::{fd_budget, networking::ContextualNetAddress};
 use kaspad_lib::args::Args;
 use tokio::task::JoinHandle;
@@ -231,6 +238,7 @@ async fn sanity_test() {
                     assert!(response.is_utxo_indexed);
                     assert!(response.has_message_id);
                     assert!(response.has_notify_command);
+                    assert!(Namespaces::from_str(&response.namespaces.join(",")).unwrap().is_enabled(&Namespace::General))
                 })
             }
 
diff --git a/wallet/core/src/tests/rpc_core_mock.rs b/wallet/core/src/tests/rpc_core_mock.rs
index 529fffffe8..d558970a5f 100644
--- a/wallet/core/src/tests/rpc_core_mock.rs
+++ b/wallet/core/src/tests/rpc_core_mock.rs
@@ -9,7 +9,7 @@ use kaspa_notify::scope::Scope;
 use kaspa_notify::subscription::context::SubscriptionContext;
 use kaspa_notify::subscription::{MutationPolicies, UtxosChangedMutationPolicy};
 use kaspa_rpc_core::api::ctl::RpcCtl;
-use kaspa_rpc_core::{api::connection::DynRpcConnection, api::rpc::RpcApi, *};
+use kaspa_rpc_core::{api::connection::DynRpcConnection, api::namespaces::Namespaces, api::rpc::RpcApi, *};
 use kaspa_rpc_core::{notify::connection::ChannelConnection, RpcResult};
 use std::sync::Arc;
 
@@ -92,6 +92,7 @@ impl RpcApi for RpcCoreMock {
             is_synced: false,
             has_notify_command: false,
             has_message_id: false,
+            namespaces: Namespaces::default().enabled_namespaces(),
         })
     }