From 809982cdc78e287bc9f3fc0dbac5e2aab2fd7f7d Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Sun, 11 Jan 2026 21:07:33 +0000 Subject: fix(config): trim whitespace from relay-owner-nsec CLI/env input When relay_owner_nsec is provided via CLI argument or environment variable (e.g., read from a file by the NixOS module), trim any leading/trailing whitespace including newlines. This matches the behavior when reading from the .relay-owner.nsec file directly. Fixes issue where NixOS module reads nsec file with 'cat', which includes the trailing newline, making the nsec invalid when passed as a CLI argument. Also reverted the tr workaround in nix/module.nix since ngit-grasp now handles this correctly. --- nix/module.nix | 3 +-- src/config.rs | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/nix/module.nix b/nix/module.nix index 60b8d5f..d820b67 100644 --- a/nix/module.nix +++ b/nix/module.nix @@ -245,8 +245,7 @@ let # Command to run ExecStart = if cfg.relayOwnerNsecFile != null then # Use nsec from file - need to use shell to read the file - # Use tr to remove trailing newline which would invalidate the nsec - "${pkgs.bash}/bin/bash -c '${ngit-grasp}/bin/ngit-grasp --relay-owner-nsec \"$(cat ${cfg.relayOwnerNsecFile} | tr -d \"\\n\")\"'" + "${pkgs.bash}/bin/bash -c '${ngit-grasp}/bin/ngit-grasp --relay-owner-nsec \"$(cat ${cfg.relayOwnerNsecFile})\"'" else # Let ngit-grasp auto-generate nsec in .relay-owner.nsec file in dataDir "${ngit-grasp}/bin/ngit-grasp"; diff --git a/src/config.rs b/src/config.rs index 74327c9..1812fe2 100644 --- a/src/config.rs +++ b/src/config.rs @@ -185,6 +185,10 @@ impl Config { // If relay_owner_nsec not provided, load from file or generate if config.relay_owner_nsec.is_none() { config.relay_owner_nsec = Some(Self::load_or_generate_relay_owner_key()?); + } else { + // If provided via CLI/env, trim any whitespace (newlines, spaces, etc.) + // This handles cases where the value is read from a file with trailing newline + config.relay_owner_nsec = config.relay_owner_nsec.map(|s| s.trim().to_string()); } Ok(config) @@ -370,6 +374,38 @@ mod tests { assert!(npub.starts_with("npub1")); } + #[test] + fn test_relay_owner_nsec_trims_whitespace() { + // Test that Config::load() trims whitespace from provided nsec + // This simulates what happens when nsec is read from a file with trailing newline + let nsec_clean = "nsec1rt5f3gfnktvd77fdarg00ff94l8j833y778ym3xlzkx89g9v5zvq4y2qee"; + let nsec_with_newline = format!("{}\n", nsec_clean); + let nsec_with_spaces = format!(" {} ", nsec_clean); + + // Test that trimming happens by directly creating config with whitespace + let mut config1 = Config::for_testing(); + config1.relay_owner_nsec = Some(nsec_with_newline.clone()); + // Simulate what Config::load() does + config1.relay_owner_nsec = config1.relay_owner_nsec.map(|s| s.trim().to_string()); + + let mut config2 = Config::for_testing(); + config2.relay_owner_nsec = Some(nsec_with_spaces.clone()); + config2.relay_owner_nsec = config2.relay_owner_nsec.map(|s| s.trim().to_string()); + + // Both should parse successfully after trimming + assert!(config1.relay_owner_keys().is_ok()); + assert!(config2.relay_owner_keys().is_ok()); + + // Both should produce the same public key + let keys1 = config1.relay_owner_keys().unwrap(); + let keys2 = config2.relay_owner_keys().unwrap(); + assert_eq!(keys1.public_key(), keys2.public_key()); + + // Verify trimmed nsec equals clean nsec + assert_eq!(config1.relay_owner_nsec.unwrap(), nsec_clean); + assert_eq!(config2.relay_owner_nsec.unwrap(), nsec_clean); + } + #[test] fn test_metrics_config_defaults() { let config = Config::for_testing(); -- cgit v1.2.3