diff --git a/Cargo.lock b/Cargo.lock index 40ee0941e..591e2520a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,6 +89,7 @@ dependencies = [ "rlimit", "serde", "tokio", + "tor-config", "tor-error", "tor-rtcompat", "tor-socksproto", @@ -105,6 +106,7 @@ name = "arti-bench" version = "0.1.0" dependencies = [ "anyhow", + "arti", "arti-client", "arti-config", "clap", @@ -193,6 +195,7 @@ name = "arti-testing" version = "0.1.0" dependencies = [ "anyhow", + "arti", "arti-client", "arti-config", "async-trait", @@ -869,18 +872,14 @@ dependencies = [ [[package]] name = "derive_builder" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd8db2d51ce241afb74f9e5cf9482de69c4b77657c2ddaf78993eda8ce2ce8c" +version = "0.11.1" dependencies = [ "derive_builder_macro", ] [[package]] name = "derive_builder_core" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf03b84d76c5bb011d983c17e2675ab49f5498f121bf1ceae5b5855b4a4dddd" +version = "0.11.1" dependencies = [ "darling", "proc-macro2", @@ -890,9 +889,7 @@ dependencies = [ [[package]] name = "derive_builder_macro" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cae44c47bca4fd1eaeda52b3d0eecb664ddcbc0d4e7b238bae7ec2c931646c0" +version = "0.11.1" dependencies = [ "derive_builder_core", "syn", diff --git a/crates/arti-bench/Cargo.toml b/crates/arti-bench/Cargo.toml index f1b5331e6..3f000f3e8 100644 --- a/crates/arti-bench/Cargo.toml +++ b/crates/arti-bench/Cargo.toml @@ -22,6 +22,7 @@ tracing = "0.1.18" tracing-subscriber = { version = "0.3.0", features = ["env-filter"] } tokio = { version = "1.7", features = ["full"] } tor-rtcompat = { path="../tor-rtcompat", version = "0.1.0", features = ["tokio", "native-tls"] } +arti = { path="../arti", version = "0.1.0"} arti-config = { path="../arti-config", version = "0.1.0"} arti-client = { package="arti-client", path = "../arti-client", version = "0.1.0"} tokio-socks = "0.5" diff --git a/crates/arti-bench/src/main.rs b/crates/arti-bench/src/main.rs index 4d68b4989..7e6832f7e 100644 --- a/crates/arti-bench/src/main.rs +++ b/crates/arti-bench/src/main.rs @@ -36,8 +36,8 @@ #![allow(clippy::unwrap_used)] use anyhow::{anyhow, Result}; +use arti::cfg::ArtiConfig; use arti_client::{IsolationToken, TorAddr, TorClient, TorClientConfig}; -use arti_config::ArtiConfig; use clap::{App, Arg}; use futures::StreamExt; use rand::distributions::Standard; diff --git a/crates/arti-config/src/lib.rs b/crates/arti-config/src/lib.rs index 43b5f03a5..2fccf2194 100644 --- a/crates/arti-config/src/lib.rs +++ b/crates/arti-config/src/lib.rs @@ -52,9 +52,8 @@ mod options; pub use cmdline::CmdLine; pub use options::{ - ApplicationConfig, ApplicationConfigBuilder, ArtiConfig, ArtiConfigBuilder, LogRotation, - LogfileConfig, LogfileConfigBuilder, LoggingConfig, LoggingConfigBuilder, ProxyConfig, - ProxyConfigBuilder, + ApplicationConfig, ApplicationConfigBuilder, LogRotation, LogfileConfig, LogfileConfigBuilder, + LoggingConfig, LoggingConfigBuilder, ProxyConfig, ProxyConfigBuilder, ARTI_DEFAULTS, }; use tor_config::CfgPath; diff --git a/crates/arti-config/src/options.rs b/crates/arti-config/src/options.rs index 8c1806393..48ff3275d 100644 --- a/crates/arti-config/src/options.rs +++ b/crates/arti-config/src/options.rs @@ -1,15 +1,13 @@ //! Handling for arti's configuration formats. -use arti_client::config::{ - SystemConfig, SystemConfigBuilder, TorClientConfig, TorClientConfigBuilder, -}; use derive_builder::Builder; use serde::Deserialize; -use std::convert::TryFrom; use tor_config::{CfgPath, ConfigBuildError}; /// Default options to use for our configuration. -pub(crate) const ARTI_DEFAULTS: &str = concat!(include_str!("./arti_defaults.toml"),); +// +// TODO should this be in `arti::cfg` ? +pub const ARTI_DEFAULTS: &str = concat!(include_str!("./arti_defaults.toml"),); /// Structure to hold our application configuration options #[derive(Deserialize, Debug, Default, Clone, Builder, Eq, PartialEq)] @@ -207,256 +205,3 @@ impl ProxyConfig { self.dns_port } } - -/// Structure to hold Arti's configuration options, whether from a -/// configuration file or the command line. -// -/// These options are declared in a public crate outside of `arti` so that other -/// applications can parse and use them, if desired. If you're only embedding -/// arti via `arti-client`, and you don't want to use Arti's configuration -/// format, use [`arti_client::TorClientConfig`] instead. -/// -/// By default, Arti will run using the default Tor network, store state and -/// cache information to a per-user set of directories shared by all -/// that user's applications, and run a SOCKS client on a local port. -/// -/// NOTE: These are NOT the final options or their final layout. Expect NO -/// stability here. -#[derive(Debug, Clone, Eq, PartialEq, Default)] -pub struct ArtiConfig { - /// Configuration for application behavior. - application: ApplicationConfig, - - /// Configuration for proxy listeners - proxy: ProxyConfig, - - /// Logging configuration - logging: LoggingConfig, - - /// Information on system resources used by Arti. - system: SystemConfig, - - /// Configuration of the actual Tor client - tor: TorClientConfig, -} - -impl TryFrom for ArtiConfig { - type Error = config::ConfigError; - fn try_from(cfg: config::Config) -> Result { - let builder: ArtiConfigBuilder = cfg.try_deserialize()?; - builder - .build() - .map_err(|e| config::ConfigError::Foreign(Box::new(e))) - } -} - -// This handwritten impl ought not to exist, but it is needed until #374 is done. -impl From for TorClientConfigBuilder { - fn from(cfg: ArtiConfigBuilder) -> TorClientConfigBuilder { - cfg.tor - } -} - -impl ArtiConfig { - /// Construct a [`TorClientConfig`] based on this configuration. - pub fn tor_client_config(&self) -> Result { - Ok(self.tor.clone()) - } - - /// Return a new ArtiConfigBuilder. - pub fn builder() -> ArtiConfigBuilder { - ArtiConfigBuilder::default() - } - - /// Return the [`ApplicationConfig`] for this configuration. - pub fn application(&self) -> &ApplicationConfig { - &self.application - } - - /// Return the [`LoggingConfig`] for this configuration. - pub fn logging(&self) -> &LoggingConfig { - &self.logging - } - - /// Return the [`ProxyConfig`] for this configuration. - pub fn proxy(&self) -> &ProxyConfig { - &self.proxy - } -} - -/// Builder object used to construct an ArtiConfig. -/// -/// Most code won't need this, and should use [`TorClientConfigBuilder`] instead. -/// -/// Unlike other builder types in Arti, this builder works by exposing an -/// inner builder for each section in the [`TorClientConfig`]. -#[derive(Default, Clone, Deserialize)] -// This ought to be replaced by a derive-builder generated struct (probably as part of #374), -// but currently derive-builder can't do this. -pub struct ArtiConfigBuilder { - /// Builder for the actual Tor client. - #[serde(flatten)] - tor: TorClientConfigBuilder, - - /// Builder for the application section - #[serde(default)] - application: ApplicationConfigBuilder, - /// Builder for the proxy section. - #[serde(default)] - proxy: ProxyConfigBuilder, - /// Builder for the logging section. - #[serde(default)] - logging: LoggingConfigBuilder, - /// Builder for system resource configuration. - #[serde(default)] - system: SystemConfigBuilder, -} - -impl ArtiConfigBuilder { - /// Try to construct a new [`ArtiConfig`] from this builder. - pub fn build(&self) -> Result { - let application = self - .application - .build() - .map_err(|e| e.within("application"))?; - let proxy = self.proxy.build().map_err(|e| e.within("proxy"))?; - let logging = self.logging.build().map_err(|e| e.within("logging"))?; - let system = self.system.build().map_err(|e| e.within("system"))?; - let tor = TorClientConfigBuilder::from(self.clone()); - let tor = tor.build()?; - Ok(ArtiConfig { - application, - proxy, - logging, - system, - tor, - }) - } - - /// Return a mutable reference to an [`ApplicationConfigBuilder`] to use in - /// configuring the Arti process. - pub fn application(&mut self) -> &mut ApplicationConfigBuilder { - &mut self.application - } - - /// Return a mutable reference to a [`ProxyConfig`] to use in - /// configuring the Arti process. - pub fn proxy(&mut self) -> &mut ProxyConfigBuilder { - &mut self.proxy - } - - /// Return a mutable reference to a - /// [`LoggingConfigBuilder`] - /// to use in configuring the Arti process. - pub fn logging(&mut self) -> &mut LoggingConfigBuilder { - &mut self.logging - } - - /// Return a mutable reference to a `TorClientConfigBuilder`. - /// to use in configuring the underlying Tor network. - /// - /// Most programs shouldn't need to alter this configuration: it's only for - /// cases when you need to use a nonstandard set of Tor directory authorities - /// and fallback caches. - pub fn tor(&mut self) -> &mut TorClientConfigBuilder { - &mut self.tor - } - - /// Return a mutable reference to a [`SystemConfigBuilder`]. - /// - /// This section controls the system parameters used by Arti. - pub fn system(&mut self) -> &mut SystemConfigBuilder { - &mut self.system - } -} - -#[cfg(test)] -mod test { - #![allow(clippy::unwrap_used)] - - use arti_client::config::dir; - use std::convert::TryInto; - use std::time::Duration; - - use super::*; - - #[test] - fn default_config() { - // TODO: this is duplicate code. - let cfg = config::Config::builder() - .add_source(config::File::from_str( - ARTI_DEFAULTS, - config::FileFormat::Toml, - )) - .build() - .unwrap(); - - let parsed: ArtiConfig = cfg.try_into().unwrap(); - let default = ArtiConfig::default(); - assert_eq!(&parsed, &default); - - // Make sure that the client configuration this gives us is the default one. - let client_config = parsed.tor_client_config().unwrap(); - let dflt_client_config = TorClientConfig::default(); - assert_eq!(&client_config, &dflt_client_config); - } - - #[test] - fn builder() { - use arti_client::config::dir::DownloadSchedule; - use tor_config::CfgPath; - let sec = std::time::Duration::from_secs(1); - - let auth = dir::Authority::builder() - .name("Fred") - .v3ident([22; 20].into()) - .build() - .unwrap(); - let fallback = dir::FallbackDir::builder() - .rsa_identity([23; 20].into()) - .ed_identity([99; 32].into()) - .orports(vec!["127.0.0.7:7".parse().unwrap()]) - .build() - .unwrap(); - - let mut bld = ArtiConfig::builder(); - bld.proxy().socks_port(Some(9999)); - bld.logging().console("warn"); - bld.tor() - .tor_network() - .authorities(vec![auth]) - .fallback_caches(vec![fallback]); - bld.tor() - .storage() - .cache_dir(CfgPath::new("/var/tmp/foo".to_owned())) - .state_dir(CfgPath::new("/var/tmp/bar".to_owned())); - bld.tor() - .download_schedule() - .retry_certs(DownloadSchedule::new(10, sec, 3)) - .retry_microdescs(DownloadSchedule::new(30, 10 * sec, 9)); - bld.tor() - .override_net_params() - .insert("wombats-per-quokka".to_owned(), 7); - bld.tor() - .path_rules() - .ipv4_subnet_family_prefix(20) - .ipv6_subnet_family_prefix(48); - bld.tor() - .preemptive_circuits() - .disable_at_threshold(12) - .initial_predicted_ports(vec![80, 443]) - .prediction_lifetime(Duration::from_secs(3600)) - .min_exit_circs_for_port(2); - bld.tor() - .circuit_timing() - .max_dirtiness(90 * sec) - .request_timeout(10 * sec) - .request_max_retries(22) - .request_loyalty(3600 * sec); - bld.tor().address_filter().allow_local_addrs(true); - - let val = bld.build().unwrap(); - - assert_ne!(val, ArtiConfig::default()); - } -} diff --git a/crates/arti-testing/Cargo.toml b/crates/arti-testing/Cargo.toml index 3e9b8690a..d6427fbe5 100644 --- a/crates/arti-testing/Cargo.toml +++ b/crates/arti-testing/Cargo.toml @@ -14,6 +14,7 @@ publish = false [features] [dependencies] +arti = { package = "arti", path = "../arti", version = "0.1.0" } arti-client = { package = "arti-client", path = "../arti-client", version = "0.1.0" } tor-rtcompat = { path = "../tor-rtcompat", version = "0.1.0" } arti-config = { path = "../arti-config", version = "0.1.0" } diff --git a/crates/arti-testing/src/main.rs b/crates/arti-testing/src/main.rs index 9842daeee..f12e084e1 100644 --- a/crates/arti-testing/src/main.rs +++ b/crates/arti-testing/src/main.rs @@ -83,8 +83,8 @@ mod config; mod rt; mod traces; +use arti::ArtiConfig; use arti_client::TorClient; -use arti_config::ArtiConfig; use futures::task::SpawnExt; use rt::badtcp::BrokenTcpProvider; use tor_rtcompat::{PreferredRuntime, Runtime, SleepProviderExt}; diff --git a/crates/arti/Cargo.toml b/crates/arti/Cargo.toml index d7eadd441..5db6f2d9a 100644 --- a/crates/arti/Cargo.toml +++ b/crates/arti/Cargo.toml @@ -23,6 +23,7 @@ journald = [ "tracing-journald" ] [dependencies] arti-client = { package="arti-client", path = "../arti-client", version = "0.1.0", default-features=false} +tor-config = { path="../tor-config", version = "0.1.0" } tor-error = { path="../tor-error", version = "0.1.0", default-features=false } tor-rtcompat = { path="../tor-rtcompat", version = "0.1.0", default-features=false } tor-socksproto = { path="../tor-socksproto", version = "0.1.0"} diff --git a/crates/arti/src/cfg.rs b/crates/arti/src/cfg.rs new file mode 100644 index 000000000..314e92028 --- /dev/null +++ b/crates/arti/src/cfg.rs @@ -0,0 +1,269 @@ +//! Configuration for the Arti command line application +// +// (Thia module is called `cfg` to avoid name clash with the `config` crate, which we use.) + +use std::convert::TryFrom; + +use serde::Deserialize; + +use arti_client::config::{SystemConfig, SystemConfigBuilder, TorClientConfigBuilder}; +use arti_client::TorClientConfig; +use arti_config::{ + ApplicationConfig, ApplicationConfigBuilder, LoggingConfig, LoggingConfigBuilder, ProxyConfig, + ProxyConfigBuilder, +}; +use tor_config::ConfigBuildError; + +/// Structure to hold Arti's configuration options, whether from a +/// configuration file or the command line. +// +/// These options are declared in a public crate outside of `arti` so that other +/// applications can parse and use them, if desired. If you're only embedding +/// arti via `arti-client`, and you don't want to use Arti's configuration +/// format, use [`arti_client::TorClientConfig`] instead. +/// +/// By default, Arti will run using the default Tor network, store state and +/// cache information to a per-user set of directories shared by all +/// that user's applications, and run a SOCKS client on a local port. +/// +/// NOTE: These are NOT the final options or their final layout. Expect NO +/// stability here. +#[derive(Debug, Clone, Eq, PartialEq, Default)] +pub struct ArtiConfig { + /// Configuration for application behavior. + application: ApplicationConfig, + + /// Configuration for proxy listeners + proxy: ProxyConfig, + + /// Logging configuration + logging: LoggingConfig, + + /// Information on system resources used by Arti. + system: SystemConfig, + + /// Configuration of the actual Tor client + tor: TorClientConfig, +} + +impl TryFrom for ArtiConfig { + type Error = config::ConfigError; + fn try_from(cfg: config::Config) -> Result { + let builder: ArtiConfigBuilder = cfg.try_deserialize()?; + builder + .build() + .map_err(|e| config::ConfigError::Foreign(Box::new(e))) + } +} + +// This handwritten impl ought not to exist, but it is needed until #374 is done. +impl From for TorClientConfigBuilder { + fn from(cfg: ArtiConfigBuilder) -> TorClientConfigBuilder { + cfg.tor + } +} + +impl ArtiConfig { + /// Construct a [`TorClientConfig`] based on this configuration. + pub fn tor_client_config(&self) -> Result { + Ok(self.tor.clone()) + } + + /// Return a new ArtiConfigBuilder. + pub fn builder() -> ArtiConfigBuilder { + ArtiConfigBuilder::default() + } + + /// Return the [`ApplicationConfig`] for this configuration. + pub fn application(&self) -> &ApplicationConfig { + &self.application + } + + /// Return the [`LoggingConfig`] for this configuration. + pub fn logging(&self) -> &LoggingConfig { + &self.logging + } + + /// Return the [`ProxyConfig`] for this configuration. + pub fn proxy(&self) -> &ProxyConfig { + &self.proxy + } +} + +/// Builder object used to construct an ArtiConfig. +/// +/// Most code won't need this, and should use [`TorClientConfigBuilder`] instead. +/// +/// Unlike other builder types in Arti, this builder works by exposing an +/// inner builder for each section in the [`TorClientConfig`]. +#[derive(Default, Clone, Deserialize)] +// This ought to be replaced by a derive-builder generated struct (probably as part of #374), +// but currently derive-builder can't do this. +pub struct ArtiConfigBuilder { + /// Builder for the actual Tor client. + #[serde(flatten)] + tor: TorClientConfigBuilder, + + /// Builder for the application section + #[serde(default)] + application: ApplicationConfigBuilder, + /// Builder for the proxy section. + #[serde(default)] + proxy: ProxyConfigBuilder, + /// Builder for the logging section. + #[serde(default)] + logging: LoggingConfigBuilder, + /// Builder for system resource configuration. + #[serde(default)] + system: SystemConfigBuilder, +} + +impl ArtiConfigBuilder { + /// Try to construct a new [`ArtiConfig`] from this builder. + pub fn build(&self) -> Result { + let application = self + .application + .build() + .map_err(|e| e.within("application"))?; + let proxy = self.proxy.build().map_err(|e| e.within("proxy"))?; + let logging = self.logging.build().map_err(|e| e.within("logging"))?; + let system = self.system.build().map_err(|e| e.within("system"))?; + let tor = TorClientConfigBuilder::from(self.clone()); + let tor = tor.build()?; + Ok(ArtiConfig { + application, + proxy, + logging, + system, + tor, + }) + } + + /// Return a mutable reference to an [`ApplicationConfigBuilder`] to use in + /// configuring the Arti process. + pub fn application(&mut self) -> &mut ApplicationConfigBuilder { + &mut self.application + } + + /// Return a mutable reference to a [`ProxyConfig`] to use in + /// configuring the Arti process. + pub fn proxy(&mut self) -> &mut ProxyConfigBuilder { + &mut self.proxy + } + + /// Return a mutable reference to a + /// [`LoggingConfigBuilder`] + /// to use in configuring the Arti process. + pub fn logging(&mut self) -> &mut LoggingConfigBuilder { + &mut self.logging + } + + /// Return a mutable reference to a `TorClientConfigBuilder`. + /// to use in configuring the underlying Tor network. + /// + /// Most programs shouldn't need to alter this configuration: it's only for + /// cases when you need to use a nonstandard set of Tor directory authorities + /// and fallback caches. + pub fn tor(&mut self) -> &mut TorClientConfigBuilder { + &mut self.tor + } + + /// Return a mutable reference to a [`SystemConfigBuilder`]. + /// + /// This section controls the system parameters used by Arti. + pub fn system(&mut self) -> &mut SystemConfigBuilder { + &mut self.system + } +} + +#[cfg(test)] +mod test { + #![allow(clippy::unwrap_used)] + + use arti_client::config::dir; + use arti_config::ARTI_DEFAULTS; + use std::convert::TryInto; + use std::time::Duration; + + use super::*; + + #[test] + fn default_config() { + // TODO: this is duplicate code. + let cfg = config::Config::builder() + .add_source(config::File::from_str( + ARTI_DEFAULTS, + config::FileFormat::Toml, + )) + .build() + .unwrap(); + + let parsed: ArtiConfig = cfg.try_into().unwrap(); + let default = ArtiConfig::default(); + assert_eq!(&parsed, &default); + + // Make sure that the client configuration this gives us is the default one. + let client_config = parsed.tor_client_config().unwrap(); + let dflt_client_config = TorClientConfig::default(); + assert_eq!(&client_config, &dflt_client_config); + } + + #[test] + fn builder() { + use arti_client::config::dir::DownloadSchedule; + use tor_config::CfgPath; + let sec = std::time::Duration::from_secs(1); + + let auth = dir::Authority::builder() + .name("Fred") + .v3ident([22; 20].into()) + .build() + .unwrap(); + let fallback = dir::FallbackDir::builder() + .rsa_identity([23; 20].into()) + .ed_identity([99; 32].into()) + .orports(vec!["127.0.0.7:7".parse().unwrap()]) + .build() + .unwrap(); + + let mut bld = ArtiConfig::builder(); + bld.proxy().socks_port(Some(9999)); + bld.logging().console("warn"); + bld.tor() + .tor_network() + .authorities(vec![auth]) + .fallback_caches(vec![fallback]); + bld.tor() + .storage() + .cache_dir(CfgPath::new("/var/tmp/foo".to_owned())) + .state_dir(CfgPath::new("/var/tmp/bar".to_owned())); + bld.tor() + .download_schedule() + .retry_certs(DownloadSchedule::new(10, sec, 3)) + .retry_microdescs(DownloadSchedule::new(30, 10 * sec, 9)); + bld.tor() + .override_net_params() + .insert("wombats-per-quokka".to_owned(), 7); + bld.tor() + .path_rules() + .ipv4_subnet_family_prefix(20) + .ipv6_subnet_family_prefix(48); + bld.tor() + .preemptive_circuits() + .disable_at_threshold(12) + .initial_predicted_ports(vec![80, 443]) + .prediction_lifetime(Duration::from_secs(3600)) + .min_exit_circs_for_port(2); + bld.tor() + .circuit_timing() + .max_dirtiness(90 * sec) + .request_timeout(10 * sec) + .request_max_retries(22) + .request_loyalty(3600 * sec); + bld.tor().address_filter().allow_local_addrs(true); + + let val = bld.build().unwrap(); + + assert_ne!(val, ArtiConfig::default()); + } +} diff --git a/crates/arti/src/lib.rs b/crates/arti/src/lib.rs index dd23900ae..409f343be 100644 --- a/crates/arti/src/lib.rs +++ b/crates/arti/src/lib.rs @@ -114,6 +114,7 @@ #![warn(clippy::unseparated_literal_suffix)] #![deny(clippy::unwrap_used)] +pub mod cfg; pub mod dns; pub mod exit; pub mod process; @@ -121,8 +122,10 @@ pub mod socks; pub mod trace; pub mod watch_cfg; +pub use cfg::{ArtiConfig, ArtiConfigBuilder}; + use arti_client::{TorClient, TorClientConfig}; -use arti_config::{default_config_file, ArtiConfig}; +use arti_config::default_config_file; use tor_rtcompat::{BlockOn, Runtime}; use anyhow::{Context, Result}; @@ -144,7 +147,7 @@ pub async fn run( socks_port: u16, dns_port: u16, config_sources: arti_config::ConfigurationSources, - arti_config: arti_config::ArtiConfig, + arti_config: ArtiConfig, client_config: TorClientConfig, ) -> Result<()> { // Using OnDemand arranges that, while we are bootstrapping, incoming connections wait diff --git a/crates/arti/src/watch_cfg.rs b/crates/arti/src/watch_cfg.rs index 49206760b..ddae1d2d9 100644 --- a/crates/arti/src/watch_cfg.rs +++ b/crates/arti/src/watch_cfg.rs @@ -8,11 +8,12 @@ use std::time::Duration; use arti_client::config::Reconfigure; use arti_client::TorClient; -use arti_config::ArtiConfig; use notify::Watcher; use tor_rtcompat::Runtime; use tracing::{debug, info, warn}; +use crate::ArtiConfig; + /// How long (worst case) should we take to learn about configuration changes? const POLL_INTERVAL: Duration = Duration::from_secs(10);