use doot_core::{Config, encryption::AgeEncryption}; use std::io::Write; use std::path::PathBuf; /// Extracts the AGE-SECRET-KEY line from identity file content (filters comments). fn extract_identity_key(raw: &str) -> String { raw.lines() .find(|line| line.starts_with("AGE-SECRET-KEY-")) .map(|s| s.trim().to_string()) .unwrap_or_else(|| raw.trim().to_string()) } /// Decrypts an age-encrypted file to stdout (default) or to a file with --output. #[tracing::instrument(skip_all, fields(file = %file.display()))] pub fn run( file: PathBuf, identity: Option, output: Option, ) -> anyhow::Result<()> { let config = Config::default(); let identity_raw = if let Some(path) = identity { std::fs::read_to_string(&path)? } else if let Ok(key) = std::env::var("DOOT_AGE_IDENTITY") { key } else if config.identity_file.exists() { std::fs::read_to_string(&config.identity_file)? } else { anyhow::bail!( "no identity specified. use --identity, DOOT_AGE_IDENTITY env var, or {}", config.identity_file.display() ); }; let identity_key = extract_identity_key(&identity_raw); tracing::debug!(file = %file.display(), "decrypting file"); let encryption = AgeEncryption::new().with_identity(&identity_key)?; let data = std::fs::read(&file)?; let decrypted = encryption.decrypt(&data)?; if let Some(out_path) = output { std::fs::write(&out_path, &decrypted)?; eprintln!("decrypted {} -> {}", file.display(), out_path.display()); } else { std::io::stdout().write_all(&decrypted)?; } Ok(()) }