From a0d4e39bdca2dcba6298eac9f2c3c23af4aef997 Mon Sep 17 00:00:00 2001 From: Ruediger Klaehn Date: Mon, 29 Jun 2026 14:26:50 +0300 Subject: [PATCH 1/6] Use portable atomic so we can run on esp32 Use iroh without default features so we don't inherit ring --- Cargo.toml | 7 +++++-- irpc-iroh/Cargo.toml | 7 ++++++- irpc-iroh/src/lib.rs | 15 +++++++-------- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 08934b5..b7a3e9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -120,7 +120,10 @@ tracing = { version = "0.1.41", default-features = false } n0-future = { version = "0.3", default-features = false } n0-error = { version = "1" } tracing-subscriber = { version = "0.3.23" } -iroh = { version = "1" } -iroh-base = { version = "1" } +# default-features = false to keep `tls-ring` (and the reqwest/portmapper/metrics +# bloat) out of the build — required for the Xtensa ESP32 target, which uses a +# pure-Rust crypto provider instead of ring. See server-esp32-spiram-dht22. +iroh = { version = "1", default-features = false } +iroh-base = { version = "1", default-features = false } noq = { version = "1", default-features = false } futures-util = { version = "0.3", features = ["sink"] } diff --git a/irpc-iroh/Cargo.toml b/irpc-iroh/Cargo.toml index 10dd914..e407a39 100644 --- a/irpc-iroh/Cargo.toml +++ b/irpc-iroh/Cargo.toml @@ -20,7 +20,12 @@ serde = { workspace = true } postcard = { workspace = true, features = ["alloc", "use-std"] } n0-error = { workspace = true } n0-future = { workspace = true } -irpc = { version = "0.17.0", path = ".." } +# AtomicU64 on 32-bit targets without native 64-bit atomics (Xtensa ESP32), as iroh does. +portable-atomic = "1" +# default-features = false drops irpc's `noq_endpoint_setup` (rcgen + noq/rustls-ring, +# both ring) — test-only cert utilities not needed by the iroh transport. Keep the +# real RPC features. Required for the ring-free Xtensa ESP32 build. +irpc = { version = "0.17.0", path = "..", default-features = false, features = ["rpc", "spans", "stream", "derive"] } iroh-base.workspace = true [target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies] diff --git a/irpc-iroh/src/lib.rs b/irpc-iroh/src/lib.rs index 7307ab2..8d7cb9c 100644 --- a/irpc-iroh/src/lib.rs +++ b/irpc-iroh/src/lib.rs @@ -1,9 +1,8 @@ -use std::{ - fmt, - future::Future, - io, - sync::{Arc, atomic::AtomicU64}, -}; +use std::{fmt, future::Future, io, sync::Arc}; + +// portable-atomic provides AtomicU64 on 32-bit targets (e.g. Xtensa ESP32) that +// lack native 64-bit atomics, same as iroh itself. +use portable_atomic::{AtomicU64, Ordering}; use iroh::{ EndpointId, @@ -221,7 +220,7 @@ impl ProtocolHandler for IrohProtocol { let handler = self.handler.clone(); let request_id = self .request_id - .fetch_add(1, std::sync::atomic::Ordering::AcqRel); + .fetch_add(1, Ordering::AcqRel); let fut = handle_connection::(&connection, handler).map_err(AcceptError::from_err); let span = trace_span!("rpc", id = request_id); fut.instrument(span).await @@ -269,7 +268,7 @@ impl ProtocolHandler for Iroh0RttProtocol { let handler = self.handler.clone(); let request_id = self .request_id - .fetch_add(1, std::sync::atomic::Ordering::AcqRel); + .fetch_add(1, Ordering::AcqRel); handle_connection::(&zrtt_conn, handler) .map_err(AcceptError::from_err) .instrument(trace_span!("rpc", id = request_id)) From 99e8f2ca9d1921eb0ba1f9f00f612153eee5c5f3 Mon Sep 17 00:00:00 2001 From: Ruediger Klaehn Date: Thu, 9 Jul 2026 10:59:25 +0300 Subject: [PATCH 2/6] Check in cargo.lock --- Cargo.lock | 106 +---------------------------------------------------- 1 file changed, 1 insertion(+), 105 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2ff1cd0..cc18fdd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -205,18 +205,6 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -[[package]] -name = "attohttpc" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e2cdb6d5ed835199484bb92bb8b3edd526effe995c61732580439c1a67e2e9" -dependencies = [ - "base64", - "http", - "log", - "url", -] - [[package]] name = "autocfg" version = "1.5.1" @@ -1140,25 +1128,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "h2" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "hashbrown" version = "0.15.5" @@ -1198,24 +1167,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2295ed2f9c31e471e1428a8f88a3f0e1f4b27c15049592138d1eebe9c35b183" dependencies = [ "async-trait", - "bytes", "cfg-if", "data-encoding", "futures-channel", "futures-io", "futures-util", - "h2", "hickory-proto", - "http", "idna", "ipnet", "jni 0.22.4", "rand 0.10.1", - "rustls", "thiserror 2.0.18", "tinyvec", "tokio", - "tokio-rustls", "tracing", "url", ] @@ -1259,12 +1223,10 @@ dependencies = [ "parking_lot", "rand 0.10.1", "resolv-conf", - "rustls", "smallvec", "system-configuration", "thiserror 2.0.18", "tokio", - "tokio-rustls", "tracing", ] @@ -1332,7 +1294,6 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "h2", "http", "http-body", "httparse", @@ -1527,26 +1488,6 @@ dependencies = [ "icu_properties", ] -[[package]] -name = "igd-next" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bac9a3c8278f43b4cd8463380f4a25653ac843e5b177e1d3eaf849cc9ba10d4d" -dependencies = [ - "attohttpc", - "bytes", - "futures", - "http", - "http-body-util", - "hyper", - "hyper-util", - "log", - "rand 0.10.1", - "tokio", - "url", - "xmltree", -] - [[package]] name = "indexmap" version = "2.14.0" @@ -1623,7 +1564,6 @@ dependencies = [ "papaya", "pin-project", "portable-atomic", - "portmapper", "rand 0.10.1", "reqwest", "rustc-hash", @@ -1814,6 +1754,7 @@ dependencies = [ "opentelemetry", "opentelemetry-otlp", "opentelemetry_sdk", + "portable-atomic", "postcard", "rand 0.9.4", "serde", @@ -2750,35 +2691,6 @@ dependencies = [ "serde", ] -[[package]] -name = "portmapper" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccc716c56a0a50f7e4e25f41446419599d47c6197cc5c9858174220e97c272e6" -dependencies = [ - "base64", - "bytes", - "derive_more", - "hyper-util", - "igd-next", - "iroh-metrics", - "libc", - "n0-error", - "n0-future", - "netwatch", - "num_enum", - "rand 0.10.1", - "serde", - "smallvec", - "socket2", - "time", - "tokio", - "tokio-util", - "tower-layer", - "tracing", - "url", -] - [[package]] name = "postcard" version = "1.1.3" @@ -3768,7 +3680,6 @@ dependencies = [ "http", "httparse", "rand 0.10.1", - "ring", "rustls-pki-types", "sha1_smol", "simdutf8", @@ -4747,21 +4658,6 @@ dependencies = [ "time", ] -[[package]] -name = "xml-rs" -version = "0.8.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" - -[[package]] -name = "xmltree" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7d8a75eaf6557bb84a65ace8609883db44a29951042ada9b393151532e41fcb" -dependencies = [ - "xml-rs", -] - [[package]] name = "yasna" version = "0.6.0" From 351a6097b6305b05631239c793182470d4c63719 Mon Sep 17 00:00:00 2001 From: Ruediger Klaehn Date: Thu, 9 Jul 2026 11:01:30 +0300 Subject: [PATCH 3/6] Remove LLM comment --- Cargo.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b7a3e9f..a8d4918 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -120,9 +120,6 @@ tracing = { version = "0.1.41", default-features = false } n0-future = { version = "0.3", default-features = false } n0-error = { version = "1" } tracing-subscriber = { version = "0.3.23" } -# default-features = false to keep `tls-ring` (and the reqwest/portmapper/metrics -# bloat) out of the build — required for the Xtensa ESP32 target, which uses a -# pure-Rust crypto provider instead of ring. See server-esp32-spiram-dht22. iroh = { version = "1", default-features = false } iroh-base = { version = "1", default-features = false } noq = { version = "1", default-features = false } From 1a8a205705b2c6a0f1a3a554681307e02027d0fc Mon Sep 17 00:00:00 2001 From: Ruediger Klaehn Date: Thu, 9 Jul 2026 11:13:46 +0300 Subject: [PATCH 4/6] Enable some tls backend for tests to work --- Cargo.lock | 27 +++++++++++++++++++++++++++ irpc-iroh/Cargo.toml | 1 + irpc-iroh/src/lib.rs | 15 +++++---------- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc18fdd..038253c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1128,6 +1128,25 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "h2" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.15.5" @@ -1167,19 +1186,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2295ed2f9c31e471e1428a8f88a3f0e1f4b27c15049592138d1eebe9c35b183" dependencies = [ "async-trait", + "bytes", "cfg-if", "data-encoding", "futures-channel", "futures-io", "futures-util", + "h2", "hickory-proto", + "http", "idna", "ipnet", "jni 0.22.4", "rand 0.10.1", + "rustls", "thiserror 2.0.18", "tinyvec", "tokio", + "tokio-rustls", "tracing", "url", ] @@ -1223,10 +1247,12 @@ dependencies = [ "parking_lot", "rand 0.10.1", "resolv-conf", + "rustls", "smallvec", "system-configuration", "thiserror 2.0.18", "tokio", + "tokio-rustls", "tracing", ] @@ -3680,6 +3706,7 @@ dependencies = [ "http", "httparse", "rand 0.10.1", + "ring", "rustls-pki-types", "sha1_smol", "simdutf8", diff --git a/irpc-iroh/Cargo.toml b/irpc-iroh/Cargo.toml index e407a39..f1fb678 100644 --- a/irpc-iroh/Cargo.toml +++ b/irpc-iroh/Cargo.toml @@ -32,6 +32,7 @@ iroh-base.workspace = true getrandom = { version = "0.3", features = ["wasm_js"] } [dev-dependencies] +iroh = { workspace = true, features = ["tls-ring"] } n0-future = { workspace = true } tracing-subscriber = { workspace = true, features = ["fmt"] } irpc-derive = { version = "0.17.0", path = "../irpc-derive" } diff --git a/irpc-iroh/src/lib.rs b/irpc-iroh/src/lib.rs index 8d7cb9c..41d4520 100644 --- a/irpc-iroh/src/lib.rs +++ b/irpc-iroh/src/lib.rs @@ -1,9 +1,5 @@ use std::{fmt, future::Future, io, sync::Arc}; -// portable-atomic provides AtomicU64 on 32-bit targets (e.g. Xtensa ESP32) that -// lack native 64-bit atomics, same as iroh itself. -use portable_atomic::{AtomicU64, Ordering}; - use iroh::{ EndpointId, endpoint::{ @@ -24,6 +20,9 @@ use irpc::{ }; use n0_error::{Result, e}; use n0_future::{TryFutureExt, future::Boxed as BoxFuture}; +// portable-atomic provides AtomicU64 on 32-bit targets (e.g. Xtensa ESP32) that +// lack native 64-bit atomics, same as iroh itself. +use portable_atomic::{AtomicU64, Ordering}; use tracing::{Instrument, debug, error_span, trace, trace_span, warn}; /// Returns a client that connects to a irpc service using an [`iroh::Endpoint`]. @@ -218,9 +217,7 @@ impl IrohProtocol { impl ProtocolHandler for IrohProtocol { async fn accept(&self, connection: Connection) -> Result<(), AcceptError> { let handler = self.handler.clone(); - let request_id = self - .request_id - .fetch_add(1, Ordering::AcqRel); + let request_id = self.request_id.fetch_add(1, Ordering::AcqRel); let fut = handle_connection::(&connection, handler).map_err(AcceptError::from_err); let span = trace_span!("rpc", id = request_id); fut.instrument(span).await @@ -266,9 +263,7 @@ impl ProtocolHandler for Iroh0RttProtocol { async fn on_accepting(&self, accepting: Accepting) -> Result { let zrtt_conn = accepting.into_0rtt(); let handler = self.handler.clone(); - let request_id = self - .request_id - .fetch_add(1, Ordering::AcqRel); + let request_id = self.request_id.fetch_add(1, Ordering::AcqRel); handle_connection::(&zrtt_conn, handler) .map_err(AcceptError::from_err) .instrument(trace_span!("rpc", id = request_id)) From 484f3b48b053ea6e0fb4e3ba4bc3a061f18329ca Mon Sep 17 00:00:00 2001 From: Ruediger Klaehn Date: Thu, 9 Jul 2026 11:13:46 +0300 Subject: [PATCH 5/6] Enable some tls backend for tests to work --- Cargo.lock | 105 +++++++++++++++++++++++++++++++++++++++++++ irpc-iroh/Cargo.toml | 1 + irpc-iroh/src/lib.rs | 15 +++---- 3 files changed, 111 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc18fdd..55c3cfe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -205,6 +205,18 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "attohttpc" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e2cdb6d5ed835199484bb92bb8b3edd526effe995c61732580439c1a67e2e9" +dependencies = [ + "base64", + "http", + "log", + "url", +] + [[package]] name = "autocfg" version = "1.5.1" @@ -1128,6 +1140,25 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "h2" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.15.5" @@ -1167,19 +1198,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2295ed2f9c31e471e1428a8f88a3f0e1f4b27c15049592138d1eebe9c35b183" dependencies = [ "async-trait", + "bytes", "cfg-if", "data-encoding", "futures-channel", "futures-io", "futures-util", + "h2", "hickory-proto", + "http", "idna", "ipnet", "jni 0.22.4", "rand 0.10.1", + "rustls", "thiserror 2.0.18", "tinyvec", "tokio", + "tokio-rustls", "tracing", "url", ] @@ -1223,10 +1259,12 @@ dependencies = [ "parking_lot", "rand 0.10.1", "resolv-conf", + "rustls", "smallvec", "system-configuration", "thiserror 2.0.18", "tokio", + "tokio-rustls", "tracing", ] @@ -1294,6 +1332,7 @@ dependencies = [ "bytes", "futures-channel", "futures-core", + "h2", "http", "http-body", "httparse", @@ -1488,6 +1527,26 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "igd-next" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de7238d487a9aff61f81b5ab41c0a841532a115a398b5fa92a2fadd0885e2581" +dependencies = [ + "attohttpc", + "bytes", + "futures", + "http", + "http-body-util", + "hyper", + "hyper-util", + "log", + "rand 0.10.1", + "tokio", + "url", + "xmltree", +] + [[package]] name = "indexmap" version = "2.14.0" @@ -1564,6 +1623,7 @@ dependencies = [ "papaya", "pin-project", "portable-atomic", + "portmapper", "rand 0.10.1", "reqwest", "rustc-hash", @@ -2691,6 +2751,35 @@ dependencies = [ "serde", ] +[[package]] +name = "portmapper" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccc716c56a0a50f7e4e25f41446419599d47c6197cc5c9858174220e97c272e6" +dependencies = [ + "base64", + "bytes", + "derive_more", + "hyper-util", + "igd-next", + "iroh-metrics", + "libc", + "n0-error", + "n0-future", + "netwatch", + "num_enum", + "rand 0.10.1", + "serde", + "smallvec", + "socket2", + "time", + "tokio", + "tokio-util", + "tower-layer", + "tracing", + "url", +] + [[package]] name = "postcard" version = "1.1.3" @@ -3680,6 +3769,7 @@ dependencies = [ "http", "httparse", "rand 0.10.1", + "ring", "rustls-pki-types", "sha1_smol", "simdutf8", @@ -4658,6 +4748,21 @@ dependencies = [ "time", ] +[[package]] +name = "xml-rs" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" + +[[package]] +name = "xmltree" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7d8a75eaf6557bb84a65ace8609883db44a29951042ada9b393151532e41fcb" +dependencies = [ + "xml-rs", +] + [[package]] name = "yasna" version = "0.6.0" diff --git a/irpc-iroh/Cargo.toml b/irpc-iroh/Cargo.toml index e407a39..1cd02f5 100644 --- a/irpc-iroh/Cargo.toml +++ b/irpc-iroh/Cargo.toml @@ -32,6 +32,7 @@ iroh-base.workspace = true getrandom = { version = "0.3", features = ["wasm_js"] } [dev-dependencies] +iroh = { workspace = true, default-features = true } n0-future = { workspace = true } tracing-subscriber = { workspace = true, features = ["fmt"] } irpc-derive = { version = "0.17.0", path = "../irpc-derive" } diff --git a/irpc-iroh/src/lib.rs b/irpc-iroh/src/lib.rs index 8d7cb9c..41d4520 100644 --- a/irpc-iroh/src/lib.rs +++ b/irpc-iroh/src/lib.rs @@ -1,9 +1,5 @@ use std::{fmt, future::Future, io, sync::Arc}; -// portable-atomic provides AtomicU64 on 32-bit targets (e.g. Xtensa ESP32) that -// lack native 64-bit atomics, same as iroh itself. -use portable_atomic::{AtomicU64, Ordering}; - use iroh::{ EndpointId, endpoint::{ @@ -24,6 +20,9 @@ use irpc::{ }; use n0_error::{Result, e}; use n0_future::{TryFutureExt, future::Boxed as BoxFuture}; +// portable-atomic provides AtomicU64 on 32-bit targets (e.g. Xtensa ESP32) that +// lack native 64-bit atomics, same as iroh itself. +use portable_atomic::{AtomicU64, Ordering}; use tracing::{Instrument, debug, error_span, trace, trace_span, warn}; /// Returns a client that connects to a irpc service using an [`iroh::Endpoint`]. @@ -218,9 +217,7 @@ impl IrohProtocol { impl ProtocolHandler for IrohProtocol { async fn accept(&self, connection: Connection) -> Result<(), AcceptError> { let handler = self.handler.clone(); - let request_id = self - .request_id - .fetch_add(1, Ordering::AcqRel); + let request_id = self.request_id.fetch_add(1, Ordering::AcqRel); let fut = handle_connection::(&connection, handler).map_err(AcceptError::from_err); let span = trace_span!("rpc", id = request_id); fut.instrument(span).await @@ -266,9 +263,7 @@ impl ProtocolHandler for Iroh0RttProtocol { async fn on_accepting(&self, accepting: Accepting) -> Result { let zrtt_conn = accepting.into_0rtt(); let handler = self.handler.clone(); - let request_id = self - .request_id - .fetch_add(1, Ordering::AcqRel); + let request_id = self.request_id.fetch_add(1, Ordering::AcqRel); handle_connection::(&zrtt_conn, handler) .map_err(AcceptError::from_err) .instrument(trace_span!("rpc", id = request_id)) From b38c04adc9a157a593e42f8fc4267603530361a5 Mon Sep 17 00:00:00 2001 From: Ruediger Klaehn Date: Fri, 10 Jul 2026 12:39:32 +0300 Subject: [PATCH 6/6] Remove backwards-looking comments Importing a crate with default features false should be the default, it does not need a comment. --- irpc-iroh/Cargo.toml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/irpc-iroh/Cargo.toml b/irpc-iroh/Cargo.toml index 1cd02f5..c1dc8f9 100644 --- a/irpc-iroh/Cargo.toml +++ b/irpc-iroh/Cargo.toml @@ -20,11 +20,7 @@ serde = { workspace = true } postcard = { workspace = true, features = ["alloc", "use-std"] } n0-error = { workspace = true } n0-future = { workspace = true } -# AtomicU64 on 32-bit targets without native 64-bit atomics (Xtensa ESP32), as iroh does. portable-atomic = "1" -# default-features = false drops irpc's `noq_endpoint_setup` (rcgen + noq/rustls-ring, -# both ring) — test-only cert utilities not needed by the iroh transport. Keep the -# real RPC features. Required for the ring-free Xtensa ESP32 build. irpc = { version = "0.17.0", path = "..", default-features = false, features = ["rpc", "spans", "stream", "derive"] } iroh-base.workspace = true @@ -32,6 +28,7 @@ iroh-base.workspace = true getrandom = { version = "0.3", features = ["wasm_js"] } [dev-dependencies] +# Tests and examples need a crypto provider, which iroh's default features supply. iroh = { workspace = true, default-features = true } n0-future = { workspace = true } tracing-subscriber = { workspace = true, features = ["fmt"] }