hashtree_cli/
fips_transport.rs1use crate::config::Config;
7use crate::storage::{HashtreeStore, StorageRouter};
8use anyhow::{Context, Result};
9use hashtree_fips_transport::{
10 bind_fips_endpoint, FipsEndpointOptions, HashtreeFipsTransport, DEFAULT_FIPS_DISCOVERY_SCOPE,
11};
12use nostr::nips::nip19::ToBech32;
13use std::sync::Arc;
14use std::time::Duration;
15use tokio::task::JoinHandle;
16
17pub type DaemonFipsTransport = HashtreeFipsTransport<StorageRouter>;
18
19pub struct DaemonFipsHandle {
20 pub transport: Arc<DaemonFipsTransport>,
21 pub endpoint_npub: String,
22 pub discovery_scope: String,
23 receiver_task: JoinHandle<()>,
24}
25
26impl DaemonFipsHandle {
27 pub fn shutdown(&self) {
28 self.receiver_task.abort();
29 }
30}
31
32pub async fn start_daemon_fips_transport(
33 config: &Config,
34 keys: &nostr::Keys,
35 store: Arc<HashtreeStore>,
36) -> Result<Option<DaemonFipsHandle>> {
37 if !config.server.enable_fips || !config.server.mode.hash_get_enabled() {
38 return Ok(None);
39 }
40
41 let active_relays = config.nostr.active_relays();
42 let relays = config.server.resolved_fips_relays(&active_relays);
43 let discovery_scope = normalized_discovery_scope(&config.server.fips_discovery_scope);
44 let identity_nsec = keys
45 .secret_key()
46 .to_bech32()
47 .context("Failed to encode daemon identity for FIPS endpoint")?;
48 let endpoint = bind_fips_endpoint(FipsEndpointOptions {
49 identity_nsec,
50 discovery_scope: discovery_scope.clone(),
51 relays,
52 enable_udp: config.server.enable_fips_udp,
53 enable_webrtc: config.server.enable_fips_webrtc,
54 udp_bind_addr: config.server.fips_udp_bind_addr.clone(),
55 udp_public: config.server.fips_udp_public,
56 udp_external_addr: config.server.fips_udp_external_addr.clone(),
57 packet_channel_capacity: 1024,
58 })
59 .await
60 .context("Failed to start FIPS endpoint")?;
61
62 let request_timeout = Duration::from_millis(config.server.fips_request_timeout_ms.max(1));
63 let transport = Arc::new(
64 HashtreeFipsTransport::new(endpoint.endpoint, store.store_arc())
65 .with_request_timeout(request_timeout)
66 .with_cache_responses(false),
67 );
68 let receiver_task = transport.start();
69
70 Ok(Some(DaemonFipsHandle {
71 transport,
72 endpoint_npub: endpoint.local_peer_id,
73 discovery_scope: endpoint.discovery_scope,
74 receiver_task,
75 }))
76}
77
78fn normalized_discovery_scope(scope: &str) -> String {
79 let scope = scope.trim();
80 if scope.is_empty() {
81 DEFAULT_FIPS_DISCOVERY_SCOPE.to_string()
82 } else {
83 scope.to_string()
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[test]
92 fn empty_discovery_scope_uses_hashtree_default() {
93 assert_eq!(
94 normalized_discovery_scope(" "),
95 DEFAULT_FIPS_DISCOVERY_SCOPE.to_string()
96 );
97 }
98}