mirror of
https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit-grasp.git
synced 2026-10-06 07:28:23 +00:00
The NixOS module expanded relayOwnerNsecFile into --relay-owner-nsec, making the private key visible through process listings and routine service diagnostics. Removing the flag at the parser boundary prevents other deployments from recreating the same exposure. Load a protected systemd credential before the environment and persistent key file, fail closed for empty or invalid configured values, and preserve auto-generation only when no identity was provisioned. The NixOS service can now execute the binary directly and generated fallback keys are created with private permissions.
113 lines
4.1 KiB
Rust
113 lines
4.1 KiB
Rust
use anyhow::Result;
|
|
use clap::Parser;
|
|
use tokio::signal;
|
|
use tracing::info;
|
|
use tracing_subscriber::{EnvFilter, FmtSubscriber};
|
|
|
|
use ngit_grasp::{cleanup_empty_repos, config::Config, nostr, server::RelayServer};
|
|
|
|
/// Top-level CLI dispatcher.
|
|
///
|
|
/// With no subcommand the binary runs the relay (all relay flags apply).
|
|
/// With a subcommand it runs the requested maintenance tool instead.
|
|
#[derive(Debug, Parser)]
|
|
#[command(author, version, about = "ngit-grasp GRASP relay", long_about = None)]
|
|
#[command(propagate_version = true)]
|
|
enum Cli {
|
|
/// Run the GRASP relay server (default when no subcommand is given).
|
|
#[command(name = "serve")]
|
|
Serve(Box<Config>),
|
|
|
|
/// Remove kind 30617/30618 events whose bare git repository is empty or missing.
|
|
///
|
|
/// Runs in dry-run mode by default. Pass --execute to make changes.
|
|
/// Stop the relay service before running with --execute.
|
|
CleanupEmptyRepos(cleanup_empty_repos::CleanupArgs),
|
|
|
|
/// Permanently eject deleted repository data from holding/archive stores.
|
|
///
|
|
/// This is an operator/admin maintenance command and is idempotent.
|
|
HoldingEject(nostr::lifecycle::HoldingEjectArgs),
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<()> {
|
|
// Load .env file before clap parses, so env vars are available.
|
|
dotenvy::dotenv().ok();
|
|
|
|
// Peek at argv[1] to decide whether a subcommand was explicitly provided.
|
|
// If not, prepend the implicit "serve" subcommand so that clap routes to Cli::Serve
|
|
// and all relay flags are parsed normally (preserving backward compatibility).
|
|
let mut args: Vec<String> = std::env::args().collect();
|
|
let known_subcommands = ["serve", "cleanup-empty-repos", "holding-eject", "help"];
|
|
let has_subcommand = args.get(1).is_some_and(|a| {
|
|
known_subcommands.contains(&a.as_str())
|
|
|| matches!(a.as_str(), "-h" | "--help" | "-V" | "--version")
|
|
});
|
|
if !has_subcommand {
|
|
args.insert(1, "serve".to_string());
|
|
}
|
|
|
|
match Cli::parse_from(args) {
|
|
Cli::CleanupEmptyRepos(cleanup_args) => cleanup_empty_repos::run(&cleanup_args).await,
|
|
Cli::HoldingEject(eject_args) => nostr::lifecycle::run_holding_eject(eject_args).await,
|
|
Cli::Serve(config) => {
|
|
let mut config = *config;
|
|
config.relay_owner_nsec = Some(Config::load_relay_owner_key()?);
|
|
run_relay(config).await
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Run the relay until an OS shutdown signal arrives.
|
|
///
|
|
/// All relay wiring lives in [`ngit_grasp::server::RelayServer`];
|
|
/// this function only owns concerns specific to the standalone binary:
|
|
/// the global tracing subscriber and signal handling.
|
|
async fn run_relay(config: Config) -> Result<()> {
|
|
// Initialize tracing with configured log level
|
|
let subscriber = FmtSubscriber::builder()
|
|
.with_env_filter(EnvFilter::new(&config.log_level))
|
|
.finish();
|
|
tracing::subscriber::set_global_default(subscriber)?;
|
|
|
|
info!("Starting ngit-grasp with log level: {}", config.log_level);
|
|
|
|
let server = RelayServer::start(config).await?;
|
|
|
|
server.run_until(shutdown_signal()).await
|
|
}
|
|
|
|
/// Resolves when the process receives SIGINT (Ctrl+C) or, on unix, SIGTERM.
|
|
async fn shutdown_signal() {
|
|
#[cfg(unix)]
|
|
{
|
|
use tokio::signal::unix::{signal as unix_signal, SignalKind};
|
|
let mut sigterm = match unix_signal(SignalKind::terminate()) {
|
|
Ok(sigterm) => sigterm,
|
|
Err(e) => {
|
|
tracing::error!("Failed to install SIGTERM handler: {}", e);
|
|
// Fall back to Ctrl+C only.
|
|
let _ = signal::ctrl_c().await;
|
|
info!("Received SIGINT (Ctrl+C), cleaning up...");
|
|
return;
|
|
}
|
|
};
|
|
|
|
tokio::select! {
|
|
_ = signal::ctrl_c() => {
|
|
info!("Received SIGINT (Ctrl+C), cleaning up...");
|
|
}
|
|
_ = sigterm.recv() => {
|
|
info!("Received SIGTERM, cleaning up...");
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(not(unix))]
|
|
{
|
|
let _ = signal::ctrl_c().await;
|
|
info!("Received SIGINT (Ctrl+C), cleaning up...");
|
|
}
|
|
}
|