diff --git a/crates/configuration/Cargo.toml b/crates/configuration/Cargo.toml
index 6672b1ca67f0087bff5bec2165f085316133db00..3995f0fb9f6b216a24efaebca89b1501b64be153 100644
--- a/crates/configuration/Cargo.toml
+++ b/crates/configuration/Cargo.toml
@@ -21,3 +21,6 @@ serde = { workspace = true, features = ["derive"] }
 toml = { workspace = true }
 serde_json = { workspace = true }
 
+# zombienet deps
+support = { workspace = true }
+
diff --git a/crates/configuration/src/network.rs b/crates/configuration/src/network.rs
index b8918a11859278fc8d8a3d8c5612bff43dc42680..31b2ff6c01c3351959b9ad693b00e307689d2974 100644
--- a/crates/configuration/src/network.rs
+++ b/crates/configuration/src/network.rs
@@ -3,6 +3,9 @@ use std::{cell::RefCell, fs, marker::PhantomData, rc::Rc};
 use anyhow::anyhow;
 use regex::Regex;
 use serde::{Deserialize, Serialize};
+use support::constants::{
+    NO_ERR_DEF_BUILDER, RELAY_NOT_NONE, RW_FAILED, THIS_IS_A_BUG, VALIDATION_CHECK, VALID_REGEX,
+};
 
 use crate::{
     global_settings::{GlobalSettings, GlobalSettingsBuilder},
@@ -10,10 +13,6 @@ use crate::{
     parachain::{self, ParachainConfig, ParachainConfigBuilder},
     relaychain::{self, RelaychainConfig, RelaychainConfigBuilder},
     shared::{
-        constants::{
-            NO_ERR_DEF_BUILDER, RELAY_NOT_NONE, RW_FAILED, THIS_IS_A_BUG, VALIDATION_CHECK,
-            VALID_REGEX,
-        },
         helpers::merge_errors_vecs,
         macros::states,
         node::NodeConfig,
diff --git a/crates/configuration/src/parachain.rs b/crates/configuration/src/parachain.rs
index 3952afda8b3c0120e0b491db5fa55677467f8fc6..e2533f0353d8e58a7d466c9d07ae088e5aa6edd3 100644
--- a/crates/configuration/src/parachain.rs
+++ b/crates/configuration/src/parachain.rs
@@ -127,6 +127,10 @@ pub struct ParachainConfig {
     genesis_state_path: Option<AssetLocation>,
     genesis_state_generator: Option<Command>,
     chain_spec_path: Option<AssetLocation>,
+    // Full _template_ command, will be rendered using [tera]
+    // and executed for generate the chain-spec.
+    // available tokens {{chainName}} / {{disableBootnodes}}
+    chain_spec_command: Option<String>,
     #[serde(rename = "cumulus_based", default = "default_as_true")]
     is_cumulus_based: bool,
     #[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)]
@@ -217,6 +221,11 @@ impl ParachainConfig {
         self.chain_spec_path.as_ref()
     }
 
+    /// The full _template_ command to genera the chain-spec
+    pub fn chain_spec_command(&self) -> Option<&str> {
+        self.chain_spec_command.as_deref()
+    }
+
     /// Whether the parachain is based on cumulus.
     pub fn is_cumulus_based(&self) -> bool {
         self.is_cumulus_based
@@ -282,6 +291,7 @@ impl<C: Context> Default for ParachainConfigBuilder<Initial, C> {
                 genesis_state_generator: None,
                 genesis_overrides: None,
                 chain_spec_path: None,
+                chain_spec_command: None,
                 is_cumulus_based: true,
                 bootnodes_addresses: vec![],
                 collators: vec![],
@@ -652,6 +662,18 @@ impl<C: Context> ParachainConfigBuilder<WithId, C> {
         )
     }
 
+    /// Set the chain-spec command _template_ for the relay chain.
+    pub fn with_chain_spec_command(self, cmd_template: impl Into<String>) -> Self {
+        Self::transition(
+            ParachainConfig {
+                chain_spec_command: Some(cmd_template.into()),
+                ..self.config
+            },
+            self.validation_context,
+            self.errors,
+        )
+    }
+
     /// Set whether the parachain is based on cumulus (true in a majority of case, except adder or undying collators).
     pub fn cumulus_based(self, choice: bool) -> Self {
         Self::transition(
@@ -1218,4 +1240,20 @@ mod tests {
             Some(&RegistrationStrategy::UsingExtrinsic)
         );
     }
+
+    #[test]
+    fn parachain_config_builder_should_works_with_chain_spec_command() {
+        const CMD_TPL: &str = "./bin/chain-spec-generator {% raw %} {{chainName}} {% endraw %}";
+        let relaychain_config = ParachainConfigBuilder::new(Default::default())
+            .with_id(2000)
+            .with_chain("some-chain")
+            .with_default_image("myrepo:myimage")
+            .with_default_command("default_command")
+            .with_chain_spec_command(CMD_TPL)
+            .with_collator(|collator| collator.with_name("collator"))
+            .build()
+            .unwrap();
+
+        assert_eq!(relaychain_config.chain_spec_command(), Some(CMD_TPL));
+    }
 }
diff --git a/crates/configuration/src/relaychain.rs b/crates/configuration/src/relaychain.rs
index 760be05fea542d76dc462b5c3e4cffa8e7686d22..e6051038a15dbc6b3f4bb6ae8311da7f58a4d3d5 100644
--- a/crates/configuration/src/relaychain.rs
+++ b/crates/configuration/src/relaychain.rs
@@ -1,10 +1,10 @@
 use std::{cell::RefCell, error::Error, fmt::Debug, marker::PhantomData, rc::Rc};
 
 use serde::{Deserialize, Serialize};
+use support::constants::{DEFAULT_TYPESTATE, THIS_IS_A_BUG};
 
 use crate::{
     shared::{
-        constants::{DEFAULT_TYPESTATE, THIS_IS_A_BUG},
         errors::{ConfigError, FieldError},
         helpers::{merge_errors, merge_errors_vecs},
         macros::states,
@@ -29,6 +29,10 @@ pub struct RelaychainConfig {
     #[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)]
     default_args: Vec<Arg>,
     chain_spec_path: Option<AssetLocation>,
+    // Full _template_ command, will be rendered using [tera]
+    // and executed for generate the chain-spec.
+    // available tokens {{chainName}} / {{disableBootnodes}}
+    chain_spec_command: Option<String>,
     random_nominators_count: Option<u32>,
     max_nominations: Option<u8>,
     #[serde(skip_serializing_if = "std::vec::Vec::is_empty", default)]
@@ -73,6 +77,11 @@ impl RelaychainConfig {
         self.chain_spec_path.as_ref()
     }
 
+    /// The full _template_ command to genera the chain-spec
+    pub fn chain_spec_command(&self) -> Option<&str> {
+        self.chain_spec_command.as_deref()
+    }
+
     /// The non-default command used for nodes.
     pub fn command(&self) -> Option<&Command> {
         self.command.as_ref()
@@ -130,6 +139,7 @@ impl Default for RelaychainConfigBuilder<Initial> {
                 default_db_snapshot: None,
                 default_args: vec![],
                 chain_spec_path: None,
+                chain_spec_command: None,
                 command: None,
                 random_nominators_count: None,
                 max_nominations: None,
@@ -313,6 +323,18 @@ impl RelaychainConfigBuilder<WithChain> {
         )
     }
 
+    /// Set the chain-spec command _template_ for the relay chain.
+    pub fn with_chain_spec_command(self, cmd_template: impl Into<String>) -> Self {
+        Self::transition(
+            RelaychainConfig {
+                chain_spec_command: Some(cmd_template.into()),
+                ..self.config
+            },
+            self.validation_context,
+            self.errors,
+        )
+    }
+
     /// Set the number of `random nominators` to create for chains using staking, this is used in tandem with `max_nominations` to simulate the amount of nominators and nominations.
     pub fn with_random_nominators_count(self, random_nominators_count: u32) -> Self {
         Self::transition(
@@ -657,4 +679,19 @@ mod tests {
             "relaychain.nodes['node'].image: 'invalid image' doesn't match regex '^([ip]|[hostname]/)?[tag_name]:[tag_version]?$'"
         );
     }
+
+    #[test]
+    fn relaychain_config_builder_should_works_with_chain_spec_command() {
+        const CMD_TPL: &str = "./bin/chain-spec-generator {% raw %} {{chainName}} {% endraw %}";
+        let relaychain_config = RelaychainConfigBuilder::new(Default::default())
+            .with_chain("polkadot")
+            .with_default_image("myrepo:myimage")
+            .with_default_command("default_command")
+            .with_chain_spec_command(CMD_TPL)
+            .with_node(|node| node.with_name("node1").bootnode(true))
+            .build()
+            .unwrap();
+
+        assert_eq!(relaychain_config.chain_spec_command(), Some(CMD_TPL));
+    }
 }
diff --git a/crates/configuration/src/shared.rs b/crates/configuration/src/shared.rs
index 36b71c43e223e632d1cb93f900efd225ec583384..bb1d7bf19f2f4ff2ba97c84a3eda393105f21e17 100644
--- a/crates/configuration/src/shared.rs
+++ b/crates/configuration/src/shared.rs
@@ -1,4 +1,3 @@
-pub mod constants;
 pub mod errors;
 pub mod helpers;
 pub mod macros;
diff --git a/crates/configuration/src/shared/helpers.rs b/crates/configuration/src/shared/helpers.rs
index ee0a65e2fb825a06f84432ee6a77e449eb721020..c111e66e04e5a0edd22f380c89f8bb5a250b069d 100644
--- a/crates/configuration/src/shared/helpers.rs
+++ b/crates/configuration/src/shared/helpers.rs
@@ -1,7 +1,8 @@
 use std::{cell::RefCell, rc::Rc};
 
+use support::constants::{BORROWABLE, THIS_IS_A_BUG};
+
 use super::{
-    constants::{BORROWABLE, THIS_IS_A_BUG},
     errors::ValidationError,
     types::{Port, ValidationContext},
 };
diff --git a/crates/configuration/src/shared/resources.rs b/crates/configuration/src/shared/resources.rs
index 79f53fb58e458535b48c01febf2b3e5d719064d4..cdd820fd8d000462d630d82969997b9603a74889 100644
--- a/crates/configuration/src/shared/resources.rs
+++ b/crates/configuration/src/shared/resources.rs
@@ -7,12 +7,12 @@ use serde::{
     ser::SerializeStruct,
     Deserialize, Serialize,
 };
+use support::constants::{SHOULD_COMPILE, THIS_IS_A_BUG};
 
 use super::{
     errors::{ConversionError, FieldError},
     helpers::merge_errors,
 };
-use crate::shared::constants::{SHOULD_COMPILE, THIS_IS_A_BUG};
 
 /// A resource quantity used to define limits (k8s/podman only).
 /// It can be constructed from a `&str` or u64, if it fails, it returns a [`ConversionError`].
diff --git a/crates/configuration/src/shared/types.rs b/crates/configuration/src/shared/types.rs
index c1ae162acdfccc5c9cd54785491ac9618220571c..23bf307465cc56484f20a361e213ca91a7d2f7d0 100644
--- a/crates/configuration/src/shared/types.rs
+++ b/crates/configuration/src/shared/types.rs
@@ -8,10 +8,10 @@ use std::{
 use lazy_static::lazy_static;
 use regex::Regex;
 use serde::{de, Deserialize, Deserializer, Serialize};
+use support::constants::{INFAILABLE, PREFIX_CANT_BE_NONE, SHOULD_COMPILE, THIS_IS_A_BUG};
 use url::Url;
 
 use super::{errors::ConversionError, resources::Resources};
-use crate::shared::constants::{INFAILABLE, PREFIX_CANT_BE_NONE, SHOULD_COMPILE, THIS_IS_A_BUG};
 
 /// An alias for a duration in seconds.
 pub type Duration = u32;
diff --git a/crates/orchestrator/src/generators/chain_spec.rs b/crates/orchestrator/src/generators/chain_spec.rs
index fd9ed40e7d9860ef7c6626d8ac0b1c0220779db2..5d44e47fe052ec32c52b5c6de925814819cee44f 100644
--- a/crates/orchestrator/src/generators/chain_spec.rs
+++ b/crates/orchestrator/src/generators/chain_spec.rs
@@ -4,14 +4,14 @@ use std::{
 };
 
 use anyhow::anyhow;
-use configuration::{shared::constants::THIS_IS_A_BUG, types::AssetLocation, HrmpChannelConfig};
+use configuration::{types::AssetLocation, HrmpChannelConfig};
 use provider::{
     constants::NODE_CONFIG_DIR,
     types::{GenerateFileCommand, GenerateFilesOptions, TransferedFile},
     DynNamespace, ProviderError,
 };
 use serde_json::json;
-use support::fs::FileSystem;
+use support::{constants::THIS_IS_A_BUG, fs::FileSystem, replacer::apply_replacements};
 use tracing::{debug, trace, warn};
 
 use super::errors::GeneratorError;
@@ -144,19 +144,38 @@ impl ChainSpec {
             }
         } else {
             // we should create the chain-spec using command.
-            // SAFETY: we ensure that command is some with the first check of the fn
-            let cmd = self.command.as_ref().unwrap();
-            let mut args: Vec<String> = vec!["build-spec".into()];
+            let mut replacement_value = String::default();
             if let Some(chain_name) = self.chain_name.as_ref() {
                 if !chain_name.is_empty() {
-                    args.push("--chain".into());
-                    args.push(chain_name.clone());
+                    replacement_value = chain_name.clone();
                 }
-            }
-            args.push("--disable-default-bootnode".into());
+            };
+
+            // SAFETY: we ensure that command is some with the first check of the fn
+            // default as empty
+            let sanitized_cmd = if replacement_value.is_empty() {
+                // we need to remove the `--chain` flag
+                self.command.as_ref().unwrap().replace("--chain", "")
+            } else {
+                self.command.as_ref().unwrap().clone()
+            };
+
+            let full_cmd = apply_replacements(
+                &sanitized_cmd,
+                &HashMap::from([("chainName", replacement_value.as_str())]),
+            );
+            trace!("full_cmd: {:?}", full_cmd);
+
+            let parts: Vec<&str> = full_cmd.split_whitespace().collect();
+            let Some((cmd, args)) = parts.split_first() else {
+                return Err(GeneratorError::ChainSpecGeneration(format!(
+                    "Invalid generator command: {full_cmd}"
+                )));
+            };
+            trace!("cmd: {:?} - args: {:?}", cmd, args);
 
             let generate_command =
-                GenerateFileCommand::new(cmd.as_str(), maybe_plain_spec_path.clone()).args(args);
+                GenerateFileCommand::new(cmd, maybe_plain_spec_path.clone()).args(args);
             let options = GenerateFilesOptions::new(vec![generate_command], self.image.clone());
             ns.generate_files(options).await?;
         }
@@ -226,13 +245,23 @@ impl ChainSpec {
             chain_spec_path_in_pod.clone()
         };
 
-        let args: Vec<String> = vec![
-            "build-spec".into(),
-            "--chain".into(),
-            chain_spec_path_in_args,
-            "--raw".into(),
-            "--disable-default-bootnode".into(),
-        ];
+        let mut full_cmd = apply_replacements(
+            cmd,
+            &HashMap::from([("chainName", chain_spec_path_in_args.as_str())]),
+        );
+
+        if !full_cmd.contains("--raw") {
+            full_cmd = format!("{full_cmd} --raw");
+        }
+        trace!("full_cmd: {:?}", full_cmd);
+
+        let parts: Vec<&str> = full_cmd.split_whitespace().collect();
+        let Some((cmd, args)) = parts.split_first() else {
+            return Err(GeneratorError::ChainSpecGeneration(format!(
+                "Invalid generator command: {full_cmd}"
+            )));
+        };
+        trace!("cmd: {:?} - args: {:?}", cmd, args);
 
         let generate_command = GenerateFileCommand::new(cmd, raw_spec_path.clone()).args(args);
         let options = GenerateFilesOptions::with_files(
diff --git a/crates/orchestrator/src/generators/command.rs b/crates/orchestrator/src/generators/command.rs
index 75a3acd2280caab9b268c9910ea993a66c35c693..1adb49031a74e205f9682df15a7482dc5db0aca1 100644
--- a/crates/orchestrator/src/generators/command.rs
+++ b/crates/orchestrator/src/generators/command.rs
@@ -1,4 +1,5 @@
-use configuration::{shared::constants::THIS_IS_A_BUG, types::Arg};
+use configuration::types::Arg;
+use support::constants::THIS_IS_A_BUG;
 
 use crate::{network_spec::node::NodeSpec, shared::constants::*};
 
diff --git a/crates/orchestrator/src/generators/keystore.rs b/crates/orchestrator/src/generators/keystore.rs
index 01edca2d2268ef126aa1a81d1c19a548c36881aa..16eae1ce81c2c69154bba961e0966e55ca4f8286 100644
--- a/crates/orchestrator/src/generators/keystore.rs
+++ b/crates/orchestrator/src/generators/keystore.rs
@@ -3,9 +3,8 @@ use std::{
     vec,
 };
 
-use configuration::shared::constants::THIS_IS_A_BUG;
 use hex::encode;
-use support::fs::FileSystem;
+use support::{constants::THIS_IS_A_BUG, fs::FileSystem};
 
 use super::errors::GeneratorError;
 use crate::{shared::types::NodeAccounts, ScopedFilesystem};
diff --git a/crates/orchestrator/src/generators/port.rs b/crates/orchestrator/src/generators/port.rs
index e2175e91a1250d666e8c0e3a1e91bc329cd55827..409061e0118360f5dd49740cb9de9fc63bea48fe 100644
--- a/crates/orchestrator/src/generators/port.rs
+++ b/crates/orchestrator/src/generators/port.rs
@@ -1,6 +1,7 @@
 use std::net::TcpListener;
 
-use configuration::shared::{constants::THIS_IS_A_BUG, types::Port};
+use configuration::shared::types::Port;
+use support::constants::THIS_IS_A_BUG;
 
 use super::errors::GeneratorError;
 use crate::shared::types::ParkedPort;
diff --git a/crates/orchestrator/src/network/parachain.rs b/crates/orchestrator/src/network/parachain.rs
index 16e38cddbec284d9a8e791c2ae62d4d97a6da986..e5a8e77aefa93a915c5c11fe02ea978209f230a4 100644
--- a/crates/orchestrator/src/network/parachain.rs
+++ b/crates/orchestrator/src/network/parachain.rs
@@ -3,11 +3,10 @@ use std::{
     str::FromStr,
 };
 
-use configuration::shared::constants::THIS_IS_A_BUG;
 use provider::types::TransferedFile;
 use subxt::{dynamic::Value, tx::TxStatus, OnlineClient, SubstrateConfig};
 use subxt_signer::{sr25519::Keypair, SecretUri};
-use support::fs::FileSystem;
+use support::{constants::THIS_IS_A_BUG, fs::FileSystem};
 use tracing::info;
 
 // use crate::generators::key::generate_pair;
diff --git a/crates/orchestrator/src/network_spec.rs b/crates/orchestrator/src/network_spec.rs
index 5ea3b0191666865d23fcaa6bcd30c665170ca6fd..15c637c06401b076ab4148bfd003f4aee0233f32 100644
--- a/crates/orchestrator/src/network_spec.rs
+++ b/crates/orchestrator/src/network_spec.rs
@@ -3,11 +3,10 @@ use std::{
     sync::Arc,
 };
 
-use configuration::{
-    shared::constants::THIS_IS_A_BUG, GlobalSettings, HrmpChannelConfig, NetworkConfig,
-};
+use configuration::{GlobalSettings, HrmpChannelConfig, NetworkConfig};
 use futures::future::try_join_all;
 use provider::{ProviderError, ProviderNamespace};
+use support::constants::THIS_IS_A_BUG;
 use tracing::debug;
 
 use crate::errors::OrchestratorError;
diff --git a/crates/orchestrator/src/network_spec/node.rs b/crates/orchestrator/src/network_spec/node.rs
index f44c531d5fe9e7d6cd3636d4ffb495f7f9d3000d..3c315d6d42a806805d6eda3136675e0ccd57f82f 100644
--- a/crates/orchestrator/src/network_spec/node.rs
+++ b/crates/orchestrator/src/network_spec/node.rs
@@ -1,11 +1,11 @@
 use configuration::shared::{
-    constants::THIS_IS_A_BUG,
     node::{EnvVar, NodeConfig},
     resources::Resources,
     types::{Arg, AssetLocation, Command, Image},
 };
 use multiaddr::Multiaddr;
 use provider::types::Port;
+use support::constants::THIS_IS_A_BUG;
 
 use crate::{
     errors::OrchestratorError,
diff --git a/crates/orchestrator/src/network_spec/parachain.rs b/crates/orchestrator/src/network_spec/parachain.rs
index a353f0fb04ade78345bb1bdb7f686fc179ad478b..da9f9b7f05a5e85c0d116e2198a7435d3466678e 100644
--- a/crates/orchestrator/src/network_spec/parachain.rs
+++ b/crates/orchestrator/src/network_spec/parachain.rs
@@ -1,4 +1,4 @@
-use std::path::PathBuf;
+use std::{collections::HashMap, path::PathBuf};
 
 use configuration::{
     shared::resources::Resources,
@@ -6,7 +6,7 @@ use configuration::{
     ParachainConfig, RegistrationStrategy,
 };
 use provider::DynNamespace;
-use support::fs::FileSystem;
+use support::{fs::FileSystem, replacer::apply_replacements};
 use tracing::debug;
 
 use super::node::NodeSpec;
@@ -16,7 +16,7 @@ use crate::{
         chain_spec::{ChainSpec, Context},
         para_artifact::*,
     },
-    shared::types::ChainDefaultContext,
+    shared::{constants::DEFAULT_CHAIN_SPEC_TPL_COMMAND, types::ChainDefaultContext},
     ScopedFilesystem,
 };
 
@@ -113,9 +113,20 @@ impl ParachainSpec {
             } else {
                 // TODO: Do we need to add the posibility to set the command to use?
                 // Currently (v1) is possible but when is set is set to the default command.
+
+                let replacements = HashMap::from([
+                    ("disableBootnodes", "--disable-default-bootnode"),
+                    ("mainCommand", main_cmd.as_str()),
+                ]);
+                let tmpl = if let Some(tmpl) = config.chain_spec_command() {
+                    apply_replacements(tmpl, &replacements)
+                } else {
+                    apply_replacements(DEFAULT_CHAIN_SPEC_TPL_COMMAND, &replacements)
+                };
+
                 Some(
                     chain_spec_builder
-                        .command(main_cmd.as_str())
+                        .command(tmpl.as_str())
                         .image(main_image.clone()),
                 )
             }
diff --git a/crates/orchestrator/src/network_spec/relaychain.rs b/crates/orchestrator/src/network_spec/relaychain.rs
index ee4a678ed6424102df42572a6476dc973bc01511..3e2f1e73e56f274f5cc545ce100cbae0f0b7cec2 100644
--- a/crates/orchestrator/src/network_spec/relaychain.rs
+++ b/crates/orchestrator/src/network_spec/relaychain.rs
@@ -1,3 +1,5 @@
+use std::collections::HashMap;
+
 use configuration::{
     shared::{
         resources::Resources,
@@ -5,12 +7,13 @@ use configuration::{
     },
     RelaychainConfig,
 };
+use support::replacer::apply_replacements;
 
 use super::node::NodeSpec;
 use crate::{
     errors::OrchestratorError,
     generators::chain_spec::{ChainSpec, Context},
-    shared::types::ChainDefaultContext,
+    shared::{constants::DEFAULT_CHAIN_SPEC_TPL_COMMAND, types::ChainDefaultContext},
 };
 
 /// A relaychain configuration spec
@@ -75,11 +78,17 @@ impl RelaychainSpec {
         let chain_spec = if let Some(chain_spec_path) = config.chain_spec_path() {
             chain_spec.asset_location(chain_spec_path.clone())
         } else {
-            // TODO: Do we need to add the posibility to set the command to use?
-            // Currently (v1) is possible but when is set is set to the default command.
-            chain_spec
-                .command(main_cmd.as_str())
-                .image(main_image.clone())
+            let replacements = HashMap::from([
+                ("disableBootnodes", "--disable-default-bootnode"),
+                ("mainCommand", main_cmd.as_str()),
+            ]);
+            let tmpl = if let Some(tmpl) = config.chain_spec_command() {
+                apply_replacements(tmpl, &replacements)
+            } else {
+                apply_replacements(DEFAULT_CHAIN_SPEC_TPL_COMMAND, &replacements)
+            };
+
+            chain_spec.command(tmpl.as_str()).image(main_image.clone())
         };
 
         // build the `node_specs`
diff --git a/crates/orchestrator/src/shared/constants.rs b/crates/orchestrator/src/shared/constants.rs
index 59e880bffa680f0cc4549aa86f03960e8325332c..70342f609ecb8c97bfa8ed9d25291b3633005e3e 100644
--- a/crates/orchestrator/src/shared/constants.rs
+++ b/crates/orchestrator/src/shared/constants.rs
@@ -6,3 +6,6 @@ pub const RPC_PORT: u16 = 9944;
 pub const RPC_HTTP_PORT: u16 = 9933;
 // P2P default port
 pub const P2P_PORT: u16 = 30333;
+// default command template to build chain-spec
+pub const DEFAULT_CHAIN_SPEC_TPL_COMMAND: &str =
+    "{{mainCommand}} build-spec --chain {{chainName}} {{disableBootnodes}}";
diff --git a/crates/orchestrator/src/spawner.rs b/crates/orchestrator/src/spawner.rs
index af355cd09ac395be3b451d670d921050f0690cfc..0bf332d3a1b8a83a67616f921f48064c88e598b1 100644
--- a/crates/orchestrator/src/spawner.rs
+++ b/crates/orchestrator/src/spawner.rs
@@ -1,14 +1,13 @@
 use std::{collections::HashMap, path::PathBuf};
 
 use anyhow::Context;
-use configuration::shared::constants::THIS_IS_A_BUG;
 use provider::{
     constants::{LOCALHOST, NODE_CONFIG_DIR, NODE_DATA_DIR, NODE_RELAY_DATA_DIR, P2P_PORT},
     shared::helpers::running_in_ci,
     types::{SpawnNodeOptions, TransferedFile},
     DynNamespace,
 };
-use support::fs::FileSystem;
+use support::{constants::THIS_IS_A_BUG, fs::FileSystem};
 use tracing::info;
 
 use crate::{
diff --git a/crates/provider/src/docker/namespace.rs b/crates/provider/src/docker/namespace.rs
index 29dbeba4121a3897ecb7bf17b8bb61f0275718ed..06c030689dc85b973407be938a7f725a2a2c5c33 100644
--- a/crates/provider/src/docker/namespace.rs
+++ b/crates/provider/src/docker/namespace.rs
@@ -6,8 +6,7 @@ use std::{
 
 use anyhow::anyhow;
 use async_trait::async_trait;
-use configuration::shared::constants::THIS_IS_A_BUG;
-use support::fs::FileSystem;
+use support::{constants::THIS_IS_A_BUG, fs::FileSystem};
 use tokio::sync::{Mutex, RwLock};
 use tracing::{debug, trace};
 use uuid::Uuid;
diff --git a/crates/provider/src/docker/node.rs b/crates/provider/src/docker/node.rs
index e85c70db26d128c4449e7971a7d378f85b489d2a..e78e6a965a9ee8482f4123fbb0201ca85c8ee479 100644
--- a/crates/provider/src/docker/node.rs
+++ b/crates/provider/src/docker/node.rs
@@ -8,9 +8,9 @@ use std::{
 
 use anyhow::anyhow;
 use async_trait::async_trait;
-use configuration::{shared::constants::THIS_IS_A_BUG, types::AssetLocation};
+use configuration::types::AssetLocation;
 use futures::future::try_join_all;
-use support::fs::FileSystem;
+use support::{constants::THIS_IS_A_BUG, fs::FileSystem};
 use tokio::{time::sleep, try_join};
 use tracing::debug;
 
diff --git a/crates/provider/src/kubernetes/client.rs b/crates/provider/src/kubernetes/client.rs
index 8aac49a18b2e107c2d5bd0ba5ce9be5bba0c2b0b..9d14dfc94aa7297da94188fa40fc11b9b548aa2c 100644
--- a/crates/provider/src/kubernetes/client.rs
+++ b/crates/provider/src/kubernetes/client.rs
@@ -4,7 +4,6 @@ use std::{
 };
 
 use anyhow::anyhow;
-use configuration::shared::constants::THIS_IS_A_BUG;
 use futures::{StreamExt, TryStreamExt};
 use k8s_openapi::api::core::v1::{
     ConfigMap, Namespace, Pod, PodSpec, PodStatus, Service, ServiceSpec,
@@ -17,6 +16,7 @@ use kube::{
     Api, Resource,
 };
 use serde::de::DeserializeOwned;
+use support::constants::THIS_IS_A_BUG;
 use tokio::{io::AsyncRead, net::TcpListener, task::JoinHandle};
 use tokio_util::compat::FuturesAsyncReadCompatExt;
 use tracing::{debug, trace};
diff --git a/crates/provider/src/kubernetes/namespace.rs b/crates/provider/src/kubernetes/namespace.rs
index 7d4f2ddd274d16aaad217057aba2147d6199cf0e..ac43623888c40b24def288599fc5be35d156df26 100644
--- a/crates/provider/src/kubernetes/namespace.rs
+++ b/crates/provider/src/kubernetes/namespace.rs
@@ -7,14 +7,13 @@ use std::{
 
 use anyhow::anyhow;
 use async_trait::async_trait;
-use configuration::shared::constants::THIS_IS_A_BUG;
 use k8s_openapi::{
     api::core::v1::{
         Container, ContainerPort, HTTPGetAction, PodSpec, Probe, ServicePort, ServiceSpec,
     },
     apimachinery::pkg::util::intstr::IntOrString,
 };
-use support::fs::FileSystem;
+use support::{constants::THIS_IS_A_BUG, fs::FileSystem, replacer::apply_replacements};
 use tokio::sync::{Mutex, RwLock};
 use tracing::{debug, trace};
 use uuid::Uuid;
@@ -23,7 +22,7 @@ use super::{client::KubernetesClient, node::KubernetesNode};
 use crate::{
     constants::NAMESPACE_PREFIX,
     kubernetes::node::KubernetesNodeOptions,
-    shared::helpers::{self, running_in_ci},
+    shared::helpers::running_in_ci,
     types::{
         GenerateFileCommand, GenerateFilesOptions, ProviderCapabilities, RunCommandOptions,
         SpawnNodeOptions,
@@ -153,7 +152,7 @@ where
     }
 
     async fn initialize_static_resources(&self) -> Result<(), ProviderError> {
-        let np_manifest = helpers::apply_replacements(
+        let np_manifest = apply_replacements(
             include_str!("./static-configs/namespace-network-policy.yaml"),
             &HashMap::from([("namespace", self.name())]),
         );
diff --git a/crates/provider/src/kubernetes/node.rs b/crates/provider/src/kubernetes/node.rs
index f8f1f4fd17121fcd22550b91d456b46c7739b2fd..402c317300150bae3bc1617a7724f05d12c52add 100644
--- a/crates/provider/src/kubernetes/node.rs
+++ b/crates/provider/src/kubernetes/node.rs
@@ -9,14 +9,11 @@ use std::{
 
 use anyhow::anyhow;
 use async_trait::async_trait;
-use configuration::{
-    shared::{constants::THIS_IS_A_BUG, resources::Resources},
-    types::AssetLocation,
-};
+use configuration::{shared::resources::Resources, types::AssetLocation};
 use futures::future::try_join_all;
 use k8s_openapi::api::core::v1::{ServicePort, ServiceSpec};
 use sha2::Digest;
-use support::fs::FileSystem;
+use support::{constants::THIS_IS_A_BUG, fs::FileSystem};
 use tokio::{sync::RwLock, task::JoinHandle, time::sleep, try_join};
 use tracing::trace;
 use url::Url;
diff --git a/crates/provider/src/native/node.rs b/crates/provider/src/native/node.rs
index 4523e818177a420731f6fb8c1ad8986a8932304a..ea2cb94d3a5972cee45e59365943063b7532539a 100644
--- a/crates/provider/src/native/node.rs
+++ b/crates/provider/src/native/node.rs
@@ -7,7 +7,7 @@ use std::{
 
 use anyhow::anyhow;
 use async_trait::async_trait;
-use configuration::{shared::constants::THIS_IS_A_BUG, types::AssetLocation};
+use configuration::types::AssetLocation;
 use flate2::read::GzDecoder;
 use futures::future::try_join_all;
 use nix::{
@@ -15,7 +15,7 @@ use nix::{
     unistd::Pid,
 };
 use sha2::Digest;
-use support::fs::FileSystem;
+use support::{constants::THIS_IS_A_BUG, fs::FileSystem};
 use tar::Archive;
 use tokio::{
     io::{AsyncRead, AsyncReadExt, BufReader},
diff --git a/crates/provider/src/shared/helpers.rs b/crates/provider/src/shared/helpers.rs
index 68983061aeef6d67f49aa85c68f5ee7099281178..58ca72b0efe2a0edf31ae900d896ed0d33681761 100644
--- a/crates/provider/src/shared/helpers.rs
+++ b/crates/provider/src/shared/helpers.rs
@@ -1,27 +1,4 @@
-use std::{collections::HashMap, env};
-
-use configuration::shared::constants::VALID_REGEX;
-use regex::{Captures, Regex};
-
-pub(crate) fn apply_replacements(text: &str, replacements: &HashMap<&str, &str>) -> String {
-    let re = Regex::new(r#"\{\{([a-zA-Z0-9_]*)\}\}"#).unwrap_or_else(|_| {
-        panic!(
-            "{} {}",
-            VALID_REGEX,
-            configuration::shared::constants::THIS_IS_A_BUG
-        )
-    });
-
-    let augmented_text = re.replace_all(text, |caps: &Captures| {
-        if let Some(replacements_value) = replacements.get(&caps[1]) {
-            replacements_value.to_string()
-        } else {
-            caps[0].to_string()
-        }
-    });
-
-    augmented_text.to_string()
-}
+use std::env;
 
 /// Check if we are running in `CI` by checking the 'RUN_IN_CI' env var
 pub fn running_in_ci() -> bool {
@@ -32,52 +9,6 @@ pub fn running_in_ci() -> bool {
 mod tests {
     use super::*;
 
-    #[test]
-    fn replace_should_works() {
-        let text = "some {{namespace}}";
-        let mut replacements = HashMap::new();
-        replacements.insert("namespace", "demo-123");
-        let res = apply_replacements(text, &replacements);
-        assert_eq!("some demo-123".to_string(), res);
-    }
-
-    #[test]
-    fn replace_multiple_should_works() {
-        let text = r#"some {{namespace}}
-        other is {{other}}"#;
-        let augmented_text = r#"some demo-123
-        other is other-123"#;
-
-        let mut replacements = HashMap::new();
-        replacements.insert("namespace", "demo-123");
-        replacements.insert("other", "other-123");
-        let res = apply_replacements(text, &replacements);
-        assert_eq!(augmented_text, res);
-    }
-
-    #[test]
-    fn replace_multiple_with_missing_should_works() {
-        let text = r#"some {{namespace}}
-        other is {{other}}"#;
-        let augmented_text = r#"some demo-123
-        other is {{other}}"#;
-
-        let mut replacements = HashMap::new();
-        replacements.insert("namespace", "demo-123");
-
-        let res = apply_replacements(text, &replacements);
-        assert_eq!(augmented_text, res);
-    }
-
-    #[test]
-    fn replace_without_replacement_should_leave_text_unchanged() {
-        let text = "some {{namespace}}";
-        let mut replacements = HashMap::new();
-        replacements.insert("other", "demo-123");
-        let res = apply_replacements(text, &replacements);
-        assert_eq!(text.to_string(), res);
-    }
-
     #[test]
     fn check_runing_in_ci_env_var() {
         assert!(!running_in_ci());
diff --git a/crates/support/Cargo.toml b/crates/support/Cargo.toml
index 1b5c097973523150b3d32b29b15efe379b268060..563fb062204503981d3c036348eaf0ccecd20228 100644
--- a/crates/support/Cargo.toml
+++ b/crates/support/Cargo.toml
@@ -22,3 +22,4 @@ tokio = { workspace = true, features = ["full"] }
 uuid = { workspace = true, features = ["v4"] }
 nix = { workspace = true, features = ["signal"] }
 rand = { workspace = true }
+regex = { workspace = true }
diff --git a/crates/configuration/src/shared/constants.rs b/crates/support/src/constants.rs
similarity index 100%
rename from crates/configuration/src/shared/constants.rs
rename to crates/support/src/constants.rs
diff --git a/crates/support/src/lib.rs b/crates/support/src/lib.rs
index 57ee38c4b3ebf08d43463c2c3aa29778bfaed2c0..b0b8e81b06cfe3faeffbbfa41182c7508b94066d 100644
--- a/crates/support/src/lib.rs
+++ b/crates/support/src/lib.rs
@@ -1,2 +1,4 @@
+pub mod constants;
 pub mod fs;
 pub mod net;
+pub mod replacer;
diff --git a/crates/support/src/replacer.rs b/crates/support/src/replacer.rs
new file mode 100644
index 0000000000000000000000000000000000000000..c2f89185ee14b76913c0a7ebda541d3419a09324
--- /dev/null
+++ b/crates/support/src/replacer.rs
@@ -0,0 +1,71 @@
+use std::collections::HashMap;
+
+use regex::{Captures, Regex};
+
+use crate::constants::{THIS_IS_A_BUG, VALID_REGEX};
+
+pub fn apply_replacements(text: &str, replacements: &HashMap<&str, &str>) -> String {
+    let re = Regex::new(r#"\{\{([a-zA-Z0-9_]*)\}\}"#)
+        .unwrap_or_else(|_| panic!("{} {}", VALID_REGEX, THIS_IS_A_BUG));
+
+    let augmented_text = re.replace_all(text, |caps: &Captures| {
+        if let Some(replacements_value) = replacements.get(&caps[1]) {
+            replacements_value.to_string()
+        } else {
+            caps[0].to_string()
+        }
+    });
+
+    augmented_text.to_string()
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn replace_should_works() {
+        let text = "some {{namespace}}";
+        let mut replacements = HashMap::new();
+        replacements.insert("namespace", "demo-123");
+        let res = apply_replacements(text, &replacements);
+        assert_eq!("some demo-123".to_string(), res);
+    }
+
+    #[test]
+    fn replace_multiple_should_works() {
+        let text = r#"some {{namespace}}
+        other is {{other}}"#;
+        let augmented_text = r#"some demo-123
+        other is other-123"#;
+
+        let mut replacements = HashMap::new();
+        replacements.insert("namespace", "demo-123");
+        replacements.insert("other", "other-123");
+        let res = apply_replacements(text, &replacements);
+        assert_eq!(augmented_text, res);
+    }
+
+    #[test]
+    fn replace_multiple_with_missing_should_works() {
+        let text = r#"some {{namespace}}
+        other is {{other}}"#;
+        let augmented_text = r#"some demo-123
+        other is {{other}}"#;
+
+        let mut replacements = HashMap::new();
+        replacements.insert("namespace", "demo-123");
+
+        let res = apply_replacements(text, &replacements);
+        assert_eq!(augmented_text, res);
+    }
+
+    #[test]
+    fn replace_without_replacement_should_leave_text_unchanged() {
+        let text = "some {{namespace}}";
+        let mut replacements = HashMap::new();
+        replacements.insert("other", "demo-123");
+        let res = apply_replacements(text, &replacements);
+        assert_eq!(text.to_string(), res);
+    }
+}