commit 8e8827a3eead103d1a9a7949c8dc3f24dce2bcb2
parent 31c079e490cbd3b0e4f0408ab4226518d20d5398
Author: triesap <tyson@radroots.org>
Date: Thu, 9 Apr 2026 03:55:32 +0000
radrootsd: extract runtime paths module
Diffstat:
5 files changed, 179 insertions(+), 165 deletions(-)
diff --git a/src/app/config.rs b/src/app/config.rs
@@ -1,21 +1,13 @@
use anyhow::{Context, Result, bail};
use radroots_nostr::prelude::RadrootsNostrMetadata;
use radroots_runtime::RadrootsNostrServiceConfig;
-use radroots_runtime_paths::{
- DEFAULT_CONFIG_FILE_NAME, DEFAULT_SERVICE_IDENTITY_FILE_NAME, RadrootsPathOverrides,
- RadrootsPathProfile, RadrootsPathResolver, RadrootsRuntimeNamespace,
-};
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
-const RADROOTSD_RUNTIME_ID: &str = "radrootsd";
-const BRIDGE_STATE_DIR_NAME: &str = "bridge";
-const BRIDGE_STATE_FILE_NAME: &str = "bridge-jobs.json";
-const RADROOTSD_PATHS_PROFILE_ENV: &str = "RADROOTSD_PATHS_PROFILE";
-const RADROOTSD_PATHS_REPO_LOCAL_ROOT_ENV: &str = "RADROOTSD_PATHS_REPO_LOCAL_ROOT";
-const RADROOTSD_DEFAULT_SHARED_SECRET_BACKEND: &str = "encrypted_file";
-const RADROOTSD_ALLOWED_PROFILES: [&str; 3] = ["interactive_user", "service_host", "repo_local"];
-const RADROOTSD_ALLOWED_SHARED_SECRET_BACKENDS: [&str; 1] = ["encrypted_file"];
+use super::paths::{
+ RadrootsdRuntimePaths, default_bridge_state_path, process_path_selection,
+ resolve_runtime_paths_with_resolver,
+};
fn default_rpc_addr() -> String {
"127.0.0.1:7070".to_string()
@@ -81,32 +73,6 @@ fn default_bridge_job_status_retention() -> usize {
256
}
-fn default_bridge_state_path() -> PathBuf {
- default_runtime_paths_for_process()
- .expect("resolve canonical radrootsd runtime paths")
- .bridge_state_path
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-struct RadrootsdRuntimePaths {
- config_path: PathBuf,
- logs_dir: PathBuf,
- identity_path: PathBuf,
- bridge_state_path: PathBuf,
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
-pub struct RadrootsdRuntimeContractOutput {
- pub active_profile: String,
- pub allowed_profiles: Vec<String>,
- pub default_shared_secret_backend: String,
- pub allowed_shared_secret_backends: Vec<String>,
- pub canonical_config_path: PathBuf,
- pub canonical_logs_dir: PathBuf,
- pub canonical_identity_path: PathBuf,
- pub canonical_bridge_state_path: PathBuf,
-}
-
#[derive(Debug, Deserialize, Clone, Default)]
struct RawServiceConfig {
#[serde(default)]
@@ -227,121 +193,10 @@ impl RawSettings {
}
}
-fn parse_path_profile(value: &str) -> Result<RadrootsPathProfile> {
- match value {
- "interactive_user" => Ok(RadrootsPathProfile::InteractiveUser),
- "service_host" => Ok(RadrootsPathProfile::ServiceHost),
- "repo_local" => Ok(RadrootsPathProfile::RepoLocal),
- _ => bail!(
- "{RADROOTSD_PATHS_PROFILE_ENV} must be `interactive_user`, `service_host`, or `repo_local`"
- ),
- }
-}
-
-fn process_path_selection() -> Result<(RadrootsPathProfile, Option<PathBuf>)> {
- let profile = match std::env::var(RADROOTSD_PATHS_PROFILE_ENV) {
- Ok(value) => parse_path_profile(&value)?,
- Err(std::env::VarError::NotPresent) => RadrootsPathProfile::InteractiveUser,
- Err(std::env::VarError::NotUnicode(_)) => {
- bail!("{RADROOTSD_PATHS_PROFILE_ENV} must be valid utf-8 when set")
- }
- };
- let repo_local_root = std::env::var_os(RADROOTSD_PATHS_REPO_LOCAL_ROOT_ENV).map(PathBuf::from);
- Ok((profile, repo_local_root))
-}
-
-fn path_overrides_for(
- profile: RadrootsPathProfile,
- repo_local_root: Option<&Path>,
-) -> Result<RadrootsPathOverrides> {
- match profile {
- RadrootsPathProfile::RepoLocal => {
- let repo_local_root = repo_local_root.context(format!(
- "{RADROOTSD_PATHS_REPO_LOCAL_ROOT_ENV} must be set when {RADROOTSD_PATHS_PROFILE_ENV}=repo_local"
- ))?;
- Ok(RadrootsPathOverrides::repo_local(repo_local_root))
- }
- _ => Ok(RadrootsPathOverrides::default()),
- }
-}
-
-fn resolve_runtime_paths_with_resolver(
- resolver: &RadrootsPathResolver,
- profile: RadrootsPathProfile,
- repo_local_root: Option<&Path>,
-) -> Result<RadrootsdRuntimePaths> {
- let namespace = RadrootsRuntimeNamespace::service(RADROOTSD_RUNTIME_ID)
- .map_err(|error| anyhow::anyhow!("resolve radrootsd namespace: {error}"))?;
- let overrides = path_overrides_for(profile, repo_local_root)?;
- let namespaced = resolver
- .resolve(profile, &overrides)
- .map_err(|error| anyhow::anyhow!("resolve radrootsd runtime paths: {error}"))?
- .namespaced(&namespace);
- Ok(RadrootsdRuntimePaths {
- config_path: namespaced.config.join(DEFAULT_CONFIG_FILE_NAME),
- logs_dir: namespaced.logs,
- identity_path: namespaced.secrets.join(DEFAULT_SERVICE_IDENTITY_FILE_NAME),
- bridge_state_path: namespaced
- .data
- .join(BRIDGE_STATE_DIR_NAME)
- .join(BRIDGE_STATE_FILE_NAME),
- })
-}
-
-fn default_runtime_paths_for_process() -> Result<RadrootsdRuntimePaths> {
- let (profile, repo_local_root) = process_path_selection()?;
- resolve_runtime_paths_with_resolver(
- &RadrootsPathResolver::current(),
- profile,
- repo_local_root.as_deref(),
- )
-}
-
-pub fn default_config_path_for_process() -> Result<PathBuf> {
- Ok(default_runtime_paths_for_process()?.config_path)
-}
-
-pub fn default_identity_path_for_process() -> Result<PathBuf> {
- Ok(default_runtime_paths_for_process()?.identity_path)
-}
-
-pub fn runtime_contract_for_process() -> Result<RadrootsdRuntimeContractOutput> {
- let (profile, repo_local_root) = process_path_selection()?;
- runtime_contract_with_resolver(
- &RadrootsPathResolver::current(),
- profile,
- repo_local_root.as_deref(),
- )
-}
-
-fn runtime_contract_with_resolver(
- resolver: &RadrootsPathResolver,
- profile: RadrootsPathProfile,
- repo_local_root: Option<&Path>,
-) -> Result<RadrootsdRuntimeContractOutput> {
- let paths = resolve_runtime_paths_with_resolver(resolver, profile, repo_local_root)?;
- Ok(RadrootsdRuntimeContractOutput {
- active_profile: profile.to_string(),
- allowed_profiles: RADROOTSD_ALLOWED_PROFILES
- .into_iter()
- .map(str::to_owned)
- .collect(),
- default_shared_secret_backend: RADROOTSD_DEFAULT_SHARED_SECRET_BACKEND.to_owned(),
- allowed_shared_secret_backends: RADROOTSD_ALLOWED_SHARED_SECRET_BACKENDS
- .into_iter()
- .map(str::to_owned)
- .collect(),
- canonical_config_path: paths.config_path,
- canonical_logs_dir: paths.logs_dir,
- canonical_identity_path: paths.identity_path,
- canonical_bridge_state_path: paths.bridge_state_path,
- })
-}
-
fn load_settings_from_path_with_resolver(
path: &Path,
- resolver: &RadrootsPathResolver,
- profile: RadrootsPathProfile,
+ resolver: &radroots_runtime_paths::RadrootsPathResolver,
+ profile: radroots_runtime_paths::RadrootsPathProfile,
repo_local_root: Option<&Path>,
) -> Result<Settings> {
let raw: RawSettings = radroots_runtime::load_required_file(path)
@@ -357,7 +212,7 @@ pub fn load_settings_from_path(path: impl AsRef<Path>) -> Result<Settings> {
let (profile, repo_local_root) = process_path_selection()?;
load_settings_from_path_with_resolver(
path,
- &RadrootsPathResolver::current(),
+ &radroots_runtime_paths::RadrootsPathResolver::current(),
profile,
repo_local_root.as_deref(),
)
@@ -534,10 +389,13 @@ impl Settings {
mod tests {
use std::path::PathBuf;
+ use crate::app::paths::{
+ default_runtime_paths_for_process, resolve_runtime_paths_with_resolver,
+ runtime_contract_with_resolver,
+ };
use super::{
BridgeConfig, BridgeDeliveryPolicy, Configuration, Nip46Config, RpcConfig,
- load_settings_from_path_with_resolver, resolve_runtime_paths_with_resolver,
- runtime_contract_with_resolver,
+ load_settings_from_path_with_resolver,
};
use radroots_runtime::RadrootsNostrServiceConfig;
use radroots_runtime_paths::{
@@ -592,8 +450,7 @@ mod tests {
#[test]
fn bridge_defaults_are_expected() {
- let paths =
- super::default_runtime_paths_for_process().expect("resolve process runtime paths");
+ let paths = default_runtime_paths_for_process().expect("resolve process runtime paths");
let cfg = BridgeConfig::default();
assert!(!cfg.enabled);
assert!(cfg.bearer_token.is_none());
diff --git a/src/app/identity_storage.rs b/src/app/identity_storage.rs
@@ -41,7 +41,7 @@ pub fn load_encrypted_identity(path: impl AsRef<Path>) -> Result<RadrootsIdentit
fn resolved_identity_path(path: Option<&Path>) -> PathBuf {
path.map(Path::to_path_buf).unwrap_or_else(|| {
- crate::app::config::default_identity_path_for_process()
+ crate::app::paths::default_identity_path_for_process()
.expect("resolve canonical radrootsd identity path")
})
}
diff --git a/src/app/mod.rs b/src/app/mod.rs
@@ -1,6 +1,7 @@
pub mod cli;
pub mod config;
mod identity_storage;
+mod paths;
mod runtime;
pub use cli::Args;
diff --git a/src/app/paths.rs b/src/app/paths.rs
@@ -0,0 +1,157 @@
+use std::path::{Path, PathBuf};
+
+use anyhow::{Context, Result, bail};
+use radroots_runtime_paths::{
+ DEFAULT_CONFIG_FILE_NAME, DEFAULT_SERVICE_IDENTITY_FILE_NAME, RadrootsPathOverrides,
+ RadrootsPathProfile, RadrootsPathResolver, RadrootsRuntimeNamespace,
+};
+use serde::Serialize;
+
+const RADROOTSD_RUNTIME_ID: &str = "radrootsd";
+const BRIDGE_STATE_DIR_NAME: &str = "bridge";
+const BRIDGE_STATE_FILE_NAME: &str = "bridge-jobs.json";
+const RADROOTSD_PATHS_PROFILE_ENV: &str = "RADROOTSD_PATHS_PROFILE";
+const RADROOTSD_PATHS_REPO_LOCAL_ROOT_ENV: &str = "RADROOTSD_PATHS_REPO_LOCAL_ROOT";
+const RADROOTSD_DEFAULT_SHARED_SECRET_BACKEND: &str = "encrypted_file";
+const RADROOTSD_ALLOWED_PROFILES: [&str; 3] = ["interactive_user", "service_host", "repo_local"];
+const RADROOTSD_ALLOWED_SHARED_SECRET_BACKENDS: [&str; 1] = ["encrypted_file"];
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub(crate) struct RadrootsdRuntimePaths {
+ pub(crate) config_path: PathBuf,
+ pub(crate) logs_dir: PathBuf,
+ pub(crate) identity_path: PathBuf,
+ pub(crate) bridge_state_path: PathBuf,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
+pub struct RadrootsdRuntimeContractOutput {
+ pub active_profile: String,
+ pub allowed_profiles: Vec<String>,
+ pub default_shared_secret_backend: String,
+ pub allowed_shared_secret_backends: Vec<String>,
+ pub canonical_config_path: PathBuf,
+ pub canonical_logs_dir: PathBuf,
+ pub canonical_identity_path: PathBuf,
+ pub canonical_bridge_state_path: PathBuf,
+}
+
+fn parse_path_profile(value: &str) -> Result<RadrootsPathProfile> {
+ match value {
+ "interactive_user" => Ok(RadrootsPathProfile::InteractiveUser),
+ "service_host" => Ok(RadrootsPathProfile::ServiceHost),
+ "repo_local" => Ok(RadrootsPathProfile::RepoLocal),
+ _ => bail!(
+ "{RADROOTSD_PATHS_PROFILE_ENV} must be `interactive_user`, `service_host`, or `repo_local`"
+ ),
+ }
+}
+
+pub(crate) fn process_path_selection() -> Result<(RadrootsPathProfile, Option<PathBuf>)> {
+ let profile = match std::env::var(RADROOTSD_PATHS_PROFILE_ENV) {
+ Ok(value) => parse_path_profile(&value)?,
+ Err(std::env::VarError::NotPresent) => RadrootsPathProfile::InteractiveUser,
+ Err(std::env::VarError::NotUnicode(_)) => {
+ bail!("{RADROOTSD_PATHS_PROFILE_ENV} must be valid utf-8 when set")
+ }
+ };
+ let repo_local_root =
+ std::env::var_os(RADROOTSD_PATHS_REPO_LOCAL_ROOT_ENV).map(PathBuf::from);
+ Ok((profile, repo_local_root))
+}
+
+fn path_overrides_for(
+ profile: RadrootsPathProfile,
+ repo_local_root: Option<&Path>,
+) -> Result<RadrootsPathOverrides> {
+ match profile {
+ RadrootsPathProfile::RepoLocal => {
+ let repo_local_root = repo_local_root.context(format!(
+ "{RADROOTSD_PATHS_REPO_LOCAL_ROOT_ENV} must be set when {RADROOTSD_PATHS_PROFILE_ENV}=repo_local"
+ ))?;
+ Ok(RadrootsPathOverrides::repo_local(repo_local_root))
+ }
+ _ => Ok(RadrootsPathOverrides::default()),
+ }
+}
+
+pub(crate) fn resolve_runtime_paths_with_resolver(
+ resolver: &RadrootsPathResolver,
+ profile: RadrootsPathProfile,
+ repo_local_root: Option<&Path>,
+) -> Result<RadrootsdRuntimePaths> {
+ let namespace = RadrootsRuntimeNamespace::service(RADROOTSD_RUNTIME_ID)
+ .map_err(|error| anyhow::anyhow!("resolve radrootsd namespace: {error}"))?;
+ let overrides = path_overrides_for(profile, repo_local_root)?;
+ let namespaced = resolver
+ .resolve(profile, &overrides)
+ .map_err(|error| anyhow::anyhow!("resolve radrootsd runtime paths: {error}"))?
+ .namespaced(&namespace);
+ Ok(RadrootsdRuntimePaths {
+ config_path: namespaced.config.join(DEFAULT_CONFIG_FILE_NAME),
+ logs_dir: namespaced.logs,
+ identity_path: namespaced.secrets.join(DEFAULT_SERVICE_IDENTITY_FILE_NAME),
+ bridge_state_path: namespaced
+ .data
+ .join(BRIDGE_STATE_DIR_NAME)
+ .join(BRIDGE_STATE_FILE_NAME),
+ })
+}
+
+pub(crate) fn default_runtime_paths_for_process() -> Result<RadrootsdRuntimePaths> {
+ let (profile, repo_local_root) = process_path_selection()?;
+ resolve_runtime_paths_with_resolver(
+ &RadrootsPathResolver::current(),
+ profile,
+ repo_local_root.as_deref(),
+ )
+}
+
+pub(crate) fn default_bridge_state_path() -> PathBuf {
+ default_runtime_paths_for_process()
+ .expect("resolve canonical radrootsd runtime paths")
+ .bridge_state_path
+}
+
+#[cfg_attr(test, allow(dead_code))]
+pub fn default_config_path_for_process() -> Result<PathBuf> {
+ Ok(default_runtime_paths_for_process()?.config_path)
+}
+
+pub fn default_identity_path_for_process() -> Result<PathBuf> {
+ Ok(default_runtime_paths_for_process()?.identity_path)
+}
+
+#[cfg_attr(test, allow(dead_code))]
+pub fn runtime_contract_for_process() -> Result<RadrootsdRuntimeContractOutput> {
+ let (profile, repo_local_root) = process_path_selection()?;
+ runtime_contract_with_resolver(
+ &RadrootsPathResolver::current(),
+ profile,
+ repo_local_root.as_deref(),
+ )
+}
+
+pub(crate) fn runtime_contract_with_resolver(
+ resolver: &RadrootsPathResolver,
+ profile: RadrootsPathProfile,
+ repo_local_root: Option<&Path>,
+) -> Result<RadrootsdRuntimeContractOutput> {
+ let paths = resolve_runtime_paths_with_resolver(resolver, profile, repo_local_root)?;
+ Ok(RadrootsdRuntimeContractOutput {
+ active_profile: profile.to_string(),
+ allowed_profiles: RADROOTSD_ALLOWED_PROFILES
+ .into_iter()
+ .map(str::to_owned)
+ .collect(),
+ default_shared_secret_backend: RADROOTSD_DEFAULT_SHARED_SECRET_BACKEND.to_owned(),
+ allowed_shared_secret_backends: RADROOTSD_ALLOWED_SHARED_SECRET_BACKENDS
+ .into_iter()
+ .map(str::to_owned)
+ .collect(),
+ canonical_config_path: paths.config_path,
+ canonical_logs_dir: paths.logs_dir,
+ canonical_identity_path: paths.identity_path,
+ canonical_bridge_state_path: paths.bridge_state_path,
+ })
+}
diff --git a/src/app/runtime.rs b/src/app/runtime.rs
@@ -5,7 +5,7 @@ use std::time::Duration;
use tracing::{info, warn};
use crate::app::identity_storage::load_service_identity;
-use crate::app::{cli, config};
+use crate::app::{cli, config, paths};
use crate::core::Radrootsd;
use crate::transport::jsonrpc;
#[cfg(not(test))]
@@ -136,7 +136,7 @@ fn load_args_and_settings() -> Result<(cli::Args, config::Settings)> {
.config
.clone()
.map(Ok)
- .unwrap_or_else(config::default_config_path_for_process)?;
+ .unwrap_or_else(paths::default_config_path_for_process)?;
let settings =
config::load_settings_from_path(&config_path).context("load configuration")?;
radroots_runtime::init_with_logs_dir(
@@ -150,7 +150,7 @@ fn load_args_and_settings() -> Result<(cli::Args, config::Settings)> {
fn runtime_startup_report(
args: &cli::Args,
settings: &config::Settings,
- contract: &config::RadrootsdRuntimeContractOutput,
+ contract: &paths::RadrootsdRuntimeContractOutput,
) -> RadrootsdRuntimeStartupReport {
RadrootsdRuntimeStartupReport {
active_profile: contract.active_profile.clone(),
@@ -341,8 +341,7 @@ pub async fn run() -> Result<()> {
#[cfg(not(test))]
{
- let contract =
- config::runtime_contract_for_process().context("resolve runtime contract")?;
+ let contract = paths::runtime_contract_for_process().context("resolve runtime contract")?;
let report = runtime_startup_report(&args, &settings, &contract);
log_runtime_startup_report(&report);
}
@@ -413,7 +412,7 @@ mod tests {
RadrootsdRuntimeStartupReport, RunWaitOutcome, run, run_bootstrap_hook, run_load_hook,
run_start_rpc_hook, run_wait_hook, runtime_startup_report,
};
- use crate::app::{cli, config};
+ use crate::app::{cli, config, paths};
use crate::core::Radrootsd;
use crate::transport::jsonrpc;
use radroots_events::kinds::KIND_LISTING;
@@ -492,8 +491,8 @@ mod tests {
}
}
- fn sample_runtime_contract() -> config::RadrootsdRuntimeContractOutput {
- config::RadrootsdRuntimeContractOutput {
+ fn sample_runtime_contract() -> paths::RadrootsdRuntimeContractOutput {
+ paths::RadrootsdRuntimeContractOutput {
active_profile: "interactive_user".to_string(),
allowed_profiles: vec![
"interactive_user".to_string(),