From 63cc5c8334ad431b750fd8e9aea6b31faa16a271 Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Fri, 27 Feb 2026 18:56:26 +0100 Subject: [PATCH 1/2] nexus-api: disable KeyRepublsiher in most tests --- examples/api/main.rs | 2 +- nexus-webapi/src/builder.rs | 53 +++++++++++++++++++++-------- nexus-webapi/src/mock.rs | 4 +-- nexus-webapi/tests/endpoints/mod.rs | 4 +-- nexus-webapi/tests/utils/server.rs | 26 ++++++++++++-- nexusd/src/launcher.rs | 2 +- 6 files changed, 67 insertions(+), 24 deletions(-) diff --git a/examples/api/main.rs b/examples/api/main.rs index 4f40078b2..59901e6fb 100644 --- a/examples/api/main.rs +++ b/examples/api/main.rs @@ -38,7 +38,7 @@ async fn main() -> Result<(), DynError> { .try_build() .await?; - NexusApiBuilder(api_context).start(None).await? + NexusApiBuilder::new(api_context).start(None).await? } }; diff --git a/nexus-webapi/src/builder.rs b/nexus-webapi/src/builder.rs index e10a0410e..a1760dc7b 100644 --- a/nexus-webapi/src/builder.rs +++ b/nexus-webapi/src/builder.rs @@ -26,47 +26,66 @@ pub const API_CONFIG_FILE_NAME: &str = "api-config.toml"; type ServerHandle = Handle; #[derive(Debug)] -pub struct NexusApiBuilder(pub ApiContext); +pub struct NexusApiBuilder { + api_context: ApiContext, + enable_key_republisher: bool, +} impl NexusApiBuilder { + pub fn new(api_context: ApiContext) -> Self { + Self { + api_context, + enable_key_republisher: true, + } + } + + /// Enables or disables the [KeyRepublisher]. + /// + /// When enabled (default), [NexusApi] will publish its pkarr packet to the DHT on startup + /// and periodically republish it every hour. When disabled, no publishing occurs. + pub fn enable_key_republisher(mut self, enable: bool) -> Self { + self.enable_key_republisher = enable; + self + } + /// Sets the service name for observability (tracing, logging, monitoring) pub fn name(mut self, name: String) -> Self { - self.0.api_config.name = name; + self.api_context.api_config.name = name; self } /// Configures the logging level for the service, determining verbosity and log output pub fn log_level(mut self, log_level: Level) -> Self { - self.0.api_config.stack.log_level = log_level; + self.api_context.api_config.stack.log_level = log_level; self } /// Sets the directory for storing static files on the server pub fn files_path(mut self, files_path: PathBuf) -> Self { - self.0.api_config.stack.files_path = files_path; + self.api_context.api_config.stack.files_path = files_path; self } /// Sets the OpenTelemetry endpoint for tracing and monitoring pub fn otlp_endpoint(mut self, otlp_endpoint: Option) -> Self { - self.0.api_config.stack.otlp_endpoint = otlp_endpoint; + self.api_context.api_config.stack.otlp_endpoint = otlp_endpoint; self } /// Sets the database configuration, including graph database and Redis settings pub fn db(mut self, db: DatabaseConfig) -> Self { - self.0.api_config.stack.db = db; + self.api_context.api_config.stack.db = db; self } /// Opens ddbb connections and initialises tracing layer (if provided in config) pub async fn init_stack(&self) -> Result<(), DynError> { - StackManager::setup(&self.0.api_config.name, &self.0.api_config.stack).await + StackManager::setup(&self.api_context.api_config.name, &self.api_context.api_config.stack).await } /// Creates and starts a [NexusApi] instance. @@ -83,7 +102,7 @@ impl NexusApiBuilder { .await .inspect_err(|e| error!("Failed to initialize stack: {e}"))?; - let nexus_api = NexusApi::start(self.0) + let nexus_api = NexusApi::start(self.api_context, self.enable_key_republisher) .await .inspect_err(|e| error!("Failed to start Nexus API: {e}"))?; @@ -110,8 +129,8 @@ pub struct NexusApi { pubky_tls_handle: ServerHandle, #[allow(dead_code)] - // Keep this alive. Republishing is stopped when the instance is dropped. - key_republisher: KeyRepublisher, + // Keep this alive if present. Republishing is stopped when the instance is dropped. + key_republisher: Option, } impl NexusApi { @@ -134,7 +153,7 @@ impl NexusApi { .try_build() .await?; - NexusApiBuilder(api_context).start(shutdown_rx).await + NexusApiBuilder::new(api_context).start(shutdown_rx).await } Err(_) => NexusApi::start_from_daemon(config_dir, shutdown_rx).await, } @@ -156,11 +175,11 @@ impl NexusApi { .try_build() .await?; - NexusApiBuilder(api_context).start(Some(shutdown_rx)).await + NexusApiBuilder::new(api_context).start(Some(shutdown_rx)).await } /// It sets up the necessary routes, binds to the specified address, and starts the Axum server - pub async fn start(ctx: ApiContext) -> Result { + pub async fn start(ctx: ApiContext, enable_key_republisher: bool) -> Result { // Create all the routes of the API let router = routes::routes(ctx.api_config.stack.files_path.clone()); debug!(?ctx.api_config, "Running NexusAPI with config"); @@ -171,8 +190,12 @@ impl NexusApi { let (pubky_tls_handle, pubky_tls_socket) = Self::start_pubky_tls_server(&ctx, router).await?; - let ks_ctx = derive_key_publisher_context(&ctx, pubky_tls_socket.port()); - let key_republisher = KeyRepublisher::start(&ks_ctx).await?; + let key_republisher = if enable_key_republisher { + let ks_ctx = derive_key_publisher_context(&ctx, pubky_tls_socket.port()); + Some(KeyRepublisher::start(&ks_ctx).await?) + } else { + None + }; Ok(NexusApi { ctx, diff --git a/nexus-webapi/src/mock.rs b/nexus-webapi/src/mock.rs index 78f35660d..8c20a3acc 100644 --- a/nexus-webapi/src/mock.rs +++ b/nexus-webapi/src/mock.rs @@ -25,7 +25,7 @@ impl MockDb { .try_build() .await .expect("Failed to create ApiContext"); - NexusApiBuilder(api_context) + NexusApiBuilder::new(api_context) .init_stack() .await .expect("Failed to initialize stack"); @@ -41,7 +41,7 @@ impl MockDb { .try_build() .await .expect("Failed to create ApiContext"); - NexusApiBuilder(api_context) + NexusApiBuilder::new(api_context) .init_stack() .await .expect("Failed to initialize stack"); diff --git a/nexus-webapi/tests/endpoints/mod.rs b/nexus-webapi/tests/endpoints/mod.rs index b486f94dc..64a323156 100644 --- a/nexus-webapi/tests/endpoints/mod.rs +++ b/nexus-webapi/tests/endpoints/mod.rs @@ -50,7 +50,7 @@ async fn test_info_endpoint() -> Result<()> { #[tokio_shared_rt::test(shared)] async fn test_pkarr_endpoint() -> Result<()> { - let test_server = TestServiceServer::get_test_server().await; + let test_server = TestServiceServer::get_test_server_with_key_republisher().await; let pubky_tls_dns_url = test_server.nexus_api.pubky_tls_dns_url(); let sdk = test_server.testnet.sdk()?; @@ -67,7 +67,7 @@ async fn test_pkarr_endpoint() -> Result<()> { #[tokio_shared_rt::test(shared)] async fn test_events_endpoint() -> Result<()> { - let test_server = TestServiceServer::get_test_server().await; + let test_server = TestServiceServer::get_test_server_with_key_republisher().await; let pubky_tls_dns_url = test_server.nexus_api.pubky_tls_dns_url(); let client = &test_server.testnet.client_builder().build()?; diff --git a/nexus-webapi/tests/utils/server.rs b/nexus-webapi/tests/utils/server.rs index 0200251b4..c88f5a5bc 100644 --- a/nexus-webapi/tests/utils/server.rs +++ b/nexus-webapi/tests/utils/server.rs @@ -14,19 +14,37 @@ pub struct TestServiceServer { } static TEST_SERVER: OnceCell = OnceCell::const_new(); +static TEST_SERVER_WITH_KEY_REPUBLISHER: OnceCell = OnceCell::const_new(); impl TestServiceServer { pub async fn get_test_server() -> &'static TestServiceServer { TEST_SERVER .get_or_init(|| async { let testnet = pubky_testnet::Testnet::new().await.unwrap(); - let nexus_api = Self::start_server(&testnet).await.unwrap(); + let nexus_api = Self::start_server(&testnet, false).await.unwrap(); TestServiceServer { nexus_api, testnet } }) .await } - async fn start_server(testnet: &pubky_testnet::Testnet) -> Result { + /// Returns a test server with the [KeyRepublisher] enabled. + /// + /// Use this in tests that access the API via the Pubky TLS DNS URL, which requires + /// the server's pkarr packet to be published to the DHT. + pub async fn get_test_server_with_key_republisher() -> &'static TestServiceServer { + TEST_SERVER_WITH_KEY_REPUBLISHER + .get_or_init(|| async { + let testnet = pubky_testnet::Testnet::new().await.unwrap(); + let nexus_api = Self::start_server(&testnet, true).await.unwrap(); + TestServiceServer { nexus_api, testnet } + }) + .await + } + + async fn start_server( + testnet: &pubky_testnet::Testnet, + enable_key_republisher: bool, + ) -> Result { let test_api_config = ApiConfig { // When we define the sockets, use local port 0 so OS assigns an available port public_addr: SocketAddr::from(([127, 0, 0, 1], 0)), @@ -42,7 +60,9 @@ impl TestServiceServer { .try_build() .await .expect("Failed to create ApiContext"); - let nexus_builder = NexusApiBuilder(api_context).files_path(get_files_dir_test_pathbuf()); + let nexus_builder = NexusApiBuilder::new(api_context) + .files_path(get_files_dir_test_pathbuf()) + .enable_key_republisher(enable_key_republisher); let (shutdown_tx, shutdown_rx) = tokio::sync::watch::channel(false); let _ = shutdown_tx.send(true); // We want the test server to return right away after start() diff --git a/nexusd/src/launcher.rs b/nexusd/src/launcher.rs index b5645f48c..07c098404 100644 --- a/nexusd/src/launcher.rs +++ b/nexusd/src/launcher.rs @@ -30,7 +30,7 @@ impl DaemonLauncher { let api_context = ApiContextBuilder::from_config_dir(config_dir.clone()) .try_build() .await?; - let nexus_webapi_builder = NexusApiBuilder(api_context); + let nexus_webapi_builder = NexusApiBuilder::new(api_context); let config = DaemonConfig::read_or_create_config_file(config_dir).await?; let nexus_watcher_builder = NexusWatcherBuilder::with_stack(config.watcher, &config.stack); From 6ecbe6dced193f9257a4cabad5f3881c25d53c70 Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Fri, 27 Feb 2026 19:07:53 +0100 Subject: [PATCH 2/2] Cargo fmt --- nexus-webapi/src/builder.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/nexus-webapi/src/builder.rs b/nexus-webapi/src/builder.rs index a1760dc7b..6f5ea01fd 100644 --- a/nexus-webapi/src/builder.rs +++ b/nexus-webapi/src/builder.rs @@ -85,7 +85,11 @@ impl NexusApiBuilder { /// Opens ddbb connections and initialises tracing layer (if provided in config) pub async fn init_stack(&self) -> Result<(), DynError> { - StackManager::setup(&self.api_context.api_config.name, &self.api_context.api_config.stack).await + StackManager::setup( + &self.api_context.api_config.name, + &self.api_context.api_config.stack, + ) + .await } /// Creates and starts a [NexusApi] instance. @@ -175,7 +179,9 @@ impl NexusApi { .try_build() .await?; - NexusApiBuilder::new(api_context).start(Some(shutdown_rx)).await + NexusApiBuilder::new(api_context) + .start(Some(shutdown_rx)) + .await } /// It sets up the necessary routes, binds to the specified address, and starts the Axum server