diff --git a/src/commands.rs b/src/commands.rs
index 3ce40227796a407e7cd83fe8e806a3363632e896..277764b2f115d52555166b96be2221fcd64a23fe 100644
--- a/src/commands.rs
+++ b/src/commands.rs
@@ -8,6 +8,7 @@ pub mod expire;
 pub mod identity;
 pub mod net_test;
 pub mod oneshot;
+pub mod publish;
 pub mod revocation;
 pub mod runtime;
 pub mod smith;
diff --git a/src/commands/publish.rs b/src/commands/publish.rs
new file mode 100644
index 0000000000000000000000000000000000000000..1608fca33cc47b2b25c1b69c344ba1e46ffc94eb
--- /dev/null
+++ b/src/commands/publish.rs
@@ -0,0 +1,73 @@
+// commands/publish.rs
+// This module handles the 'publish' command of the CLI.
+
+use crate::GcliError; // Custom error type.
+use anyhow::anyhow;
+use std::fs; // For file system operations.
+use std::io::{self, Write}; // For input/output operations.
+use std::process::Command; // For executing system commands. // For better error handling.
+
+/// Asks the user for confirmation before proceeding.
+fn confirm_action(version: &str) -> Result<bool, GcliError> {
+	print!(
+		"Are you sure you want to publish tag version {}? (y/N) ",
+		version
+	);
+	io::stdout()
+		.flush()
+		.map_err(|e| GcliError::Anyhow(anyhow!(e)))?; // Ensure the output is displayed immediately.
+
+	let mut response = String::new();
+	io::stdin()
+		.read_line(&mut response)
+		.map_err(|e| GcliError::Anyhow(anyhow!(e)))?;
+
+	Ok(response.trim().eq_ignore_ascii_case("y")) // Checks if the user's input is 'y' (case insensitive).
+}
+
+/// Executes the 'publish' operation.
+pub async fn handle_command() -> Result<(), GcliError> {
+	// Step 1: Read the version number from Cargo.toml
+	let cargo_toml_content =
+		fs::read_to_string("Cargo.toml").map_err(|e| GcliError::Anyhow(anyhow!(e)))?;
+	let version = cargo_toml_content
+		.lines()
+		.find(|line| line.starts_with("version ="))
+		.and_then(|line| line.split('"').nth(1))
+		.ok_or_else(|| GcliError::Input("Version not found in Cargo.toml".to_string()))?;
+
+	// Step 2: Check if the git tag already exists
+	let tag_check_output = Command::new("git")
+		.args(["tag", "-l", &format!("{}", version)])
+		.output()
+		.map_err(|e| GcliError::Anyhow(anyhow!(e)))?;
+
+	if !tag_check_output.stdout.is_empty() {
+		return Err(GcliError::Logic(format!("Tag {} already exists", version)));
+	}
+
+	// Display a confirmation prompt with the version number.
+	if !confirm_action(&version)? {
+		println!("Publication cancelled.");
+		return Ok(());
+	}
+
+	// Step 3: Create and push the git tag
+	Command::new("git")
+		.args([
+			"tag",
+			"-a",
+			&format!("{}", version),
+			"-m",
+			&format!("Release v{}", version),
+		])
+		.status()
+		.map_err(|e| GcliError::Anyhow(anyhow!(e)))?;
+
+	Command::new("git")
+		.args(["push", "origin", &format!("{}", version)])
+		.status()
+		.map_err(|e| GcliError::Anyhow(anyhow!(e)))?;
+
+	Ok(())
+}
diff --git a/src/main.rs b/src/main.rs
index eb0241866ce02b46fa833eaba50548f2ca3788ee..3a101f875358bb494a44604f7768877530d1c3f3 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -100,6 +100,8 @@ pub enum Subcommand {
 	/// Cesium
 	#[clap(subcommand, hide = true)]
 	Cesium(commands::cesium::Subcommand),
+	/// Publish a new git tag with actual version
+	Publish,
 }
 
 /// main function
@@ -135,6 +137,7 @@ async fn main() -> Result<(), GcliError> {
 		Subcommand::Indexer(subcommand) => indexer::handle_command(data, subcommand).await,
 		Subcommand::Config(subcommand) => conf::handle_command(data, subcommand),
 		Subcommand::Cesium(subcommand) => commands::cesium::handle_command(data, subcommand).await,
+		Subcommand::Publish => commands::publish::handle_command().await,
 	};
 	if let Err(ref e) = result {
 		println!("{}", e)