#![doc = include_str!("../README.md")] use std::{ collections::HashMap, path::{Path, PathBuf}, }; pub use keyfork_mnemonic::Mnemonic; pub use tower::ServiceBuilder; #[cfg(feature = "tracing")] use tracing::debug; #[cfg(feature = "tracing")] use tracing_subscriber::{ filter::{EnvFilter, LevelFilter}, fmt::{format::FmtSpan, layer}, prelude::*, registry, }; /// Errors occurring while starting Keyforkd. pub mod error; /// Middleware used by Keyforkd. pub mod middleware; pub mod server; pub mod service; pub use error::Keyforkd as KeyforkdError; pub use server::UnixServer; pub use service::Keyforkd; pub mod test_util; /// Set up a Tracing subscriber, defaulting to debug mode. #[cfg(feature = "tracing")] pub fn setup_registry() { let envfilter = EnvFilter::builder() .with_default_directive(LevelFilter::DEBUG.into()) .from_env_lossy(); registry() .with(envfilter) .with(layer().with_span_events(FmtSpan::CLOSE)) .with(tracing_error::ErrorLayer::default()) .init(); } /// Start and run a server on a given socket path. /// /// # Errors /// The function may return an error if a socket can't be bound, if the service can't be created, /// or if the server encounters an unrecoverable error while running. pub async fn start_and_run_server_on( mnemonic: Mnemonic, socket_path: &Path, ) -> Result<(), Box> { let service = ServiceBuilder::new() .layer(middleware::BincodeLayer::new()) // TODO: passphrase support and/or store passphrase with mnemonic .service(Keyforkd::new(mnemonic.generate_seed(None).to_vec())); let mut server = match UnixServer::bind(socket_path) { Ok(s) => s, Err(e) => { #[cfg(feature = "tracing")] debug!(%e, "Encountered error attempting to bind socket: {}", socket_path.display()); return Err(e.into()); } }; match server.run(service).await { #[allow(clippy::ignored_unit_patterns)] Ok(_) => (), Err(e) => { #[cfg(feature = "tracing")] debug!(%e, "Encountered error while running"); } } Ok(()) } /// Start and run the server using a discovered socket location. /// /// # Errors /// The function may return an error if the socket location could not be guessed, if a socket can't /// be bound, if the service can't be created, or if the server encounters an unrecoverable error /// while running. pub async fn start_and_run_server(mnemonic: Mnemonic) -> Result<(), Box> { let runtime_vars = std::env::vars() .filter(|(key, _)| ["XDG_RUNTIME_DIR", "KEYFORKD_SOCKET_PATH"].contains(&key.as_str())) .collect::>(); let runtime_path = if let Some(occupied) = runtime_vars.get("KEYFORKD_SOCKET_PATH") { PathBuf::from(occupied) } else { let mut runtime_path = PathBuf::from( runtime_vars .get("XDG_RUNTIME_DIR") .ok_or(KeyforkdError::NoSocketPath)?, ); runtime_path.push("keyforkd"); #[cfg(feature = "tracing")] debug!("ensuring directory exists: {}", runtime_path.display()); if !runtime_path.is_dir() { tokio::fs::create_dir(&runtime_path).await?; } runtime_path.push("keyforkd.sock"); runtime_path }; #[cfg(feature = "tracing")] debug!( "binding UNIX socket in runtime dir: {}", runtime_path.display() ); start_and_run_server_on(mnemonic, &runtime_path).await }