Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ rust-version = "1.89"
anyhow = "1.0.95"
derive_more = { version = "2.0.1", features = ["display", "from"] }
ed25519-dalek = { version = "=3.0.0-pre.6" }
irpc = { version = "0.14.0", default-features = false, features = ["derive", "stream", "spans"] }
irpc-iroh = "0.14"
iroh = { version = "0.98", default-features = false }
# irpc = { version = "0.14.0", default-features = false, features = ["derive", "stream", "spans"] }
irpc = { path = "../irpc" }
# irpc-iroh = "0.14"
irpc-iroh = { path = "../irpc/irpc-iroh" }
# iroh = { version = "0.98", default-features = false }
iroh = { git = "https://github.com/n0-computer/iroh", branch = "Frando/relay-client-auth", default-features = false }
iroh-metrics = { version = "0.38", default-features = false }
iroh-tickets = "0.5"
n0-error = "0.1"
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ mod client_host;
pub mod api_secret;
pub mod caps;
pub mod net_diagnostics;
pub mod preset;
pub mod protocol;

mod built_info {
Expand All @@ -60,5 +61,6 @@ pub use self::{
api_secret::ApiSecret,
client::{API_SECRET_ENV_VAR_NAME, Client, ClientBuilder},
net_diagnostics::{DiagnosticsReport, checks::run_diagnostics},
preset::{IrohServicesPreset, PresetBuilder, preset},
protocol::ALPN,
};
137 changes: 137 additions & 0 deletions src/preset.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
//! An [`iroh::endpoint`] preset tailored for use with iroh-services.
//!
//! [`IrohServicesPreset`] starts from the n0 stock preset (production crypto
//! provider + n0 DNS-based address lookup) and overlays the bits that
//! iroh-services callers usually want to configure together: the relay map
//! the endpoint should use, an optional explicit [`SecretKey`], and an
//! optional [`ApiSecret`] that downstream code can retrieve to wire up a
//! [`crate::Client`].
//!
//! # Example
//! ```no_run
//! use iroh::Endpoint;
//! use iroh_services::preset::preset;
//!
//! # async fn run() -> anyhow::Result<()> {
//! let endpoint = Endpoint::builder(preset().build()).bind().await?;
//! # Ok(()) }
//! ```
use anyhow::Context;
use iroh::{RelayMap, RelayMode, RelayUrl, SecretKey, endpoint::presets::Preset};

use crate::ApiSecret;

/// An iroh endpoint preset configured for iroh-services. Build one with
/// [`preset`] or [`IrohServicesPreset::builder`], then pass it to
/// [`iroh::Endpoint::builder`].
#[derive(Debug, Clone)]
pub struct IrohServicesPreset {
secret_key: Option<SecretKey>,
relays: RelayMap,
api_secret: Option<ApiSecret>,
}

impl IrohServicesPreset {
/// Start a new builder seeded with iroh-services defaults. Equivalent to
/// the free-standing [`preset`] function.
pub fn builder() -> PresetBuilder {
preset()
}

/// Returns the [`ApiSecret`] stashed on this preset, if one was set.
/// Useful for handing the same secret to a [`crate::Client`] without
/// plumbing it through twice.
pub fn api_secret(&self) -> Option<&ApiSecret> {
self.api_secret.as_ref()
}
}

impl Preset for IrohServicesPreset {
fn apply(self, builder: iroh::endpoint::Builder) -> iroh::endpoint::Builder {
// Inherit n0 defaults (crypto provider + DNS address lookup), then
// overlay our relay map and (optionally) an explicit secret key.
let mut builder = iroh::endpoint::presets::N0.apply(builder);
builder = builder.relay_mode(RelayMode::Custom(self.relays));
if let Some(sk) = self.secret_key {
builder = builder.secret_key(sk);
}
builder
}
}

/// Fluent builder for [`IrohServicesPreset`]. Construct one through
/// [`preset`] or [`IrohServicesPreset::builder`].
#[derive(Debug, Clone)]
pub struct PresetBuilder {
secret_key: Option<SecretKey>,
relays: RelayMap,
api_secret: Option<ApiSecret>,
}

/// Start a new [`IrohServicesPreset`] builder seeded with iroh-services
/// defaults: the n0 production relay map and no explicit secret key (the
/// endpoint will generate one at bind time).
pub fn preset() -> PresetBuilder {
PresetBuilder {
secret_key: None,
relays: RelayMode::Default.relay_map(),
api_secret: None,
}
}

impl PresetBuilder {
/// Set the endpoint's long-lived [`SecretKey`]. If left unset the
/// endpoint will generate a fresh random key at bind time.
pub fn secret_key(mut self, secret_key: SecretKey) -> Self {
self.secret_key = Some(secret_key);
self
}

/// Replace the default relay map with a custom set of relay URLs.
/// Each entry is parsed into a [`RelayUrl`]; any failure aborts the call.
pub fn relays<I, S>(mut self, relays: I) -> anyhow::Result<Self>
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
let parsed = relays
.into_iter()
.map(|s| {
let s = s.as_ref();
s.parse::<RelayUrl>()
.with_context(|| format!("invalid relay url {s:?}"))
})
.collect::<anyhow::Result<Vec<_>>>()?;
self.relays = RelayMap::from_iter(parsed);
Ok(self)
}

/// Pick relays via a [`RelayMode`] (e.g. `RelayMode::Staging` or a
/// pre-built `RelayMode::Custom(RelayMap)`).
pub fn relay_mode(mut self, mode: RelayMode) -> Self {
self.relays = mode.relay_map();
self
}

/// Pass in a [`RelayMap`] directly, bypassing URL parsing.
pub fn relay_map(mut self, map: RelayMap) -> Self {
self.relays = map;
self
}

/// Stash an [`ApiSecret`] on the preset so callers can retrieve it later
/// via [`IrohServicesPreset::api_secret`] when constructing a client.
pub fn api_secret(mut self, api_secret: ApiSecret) -> Self {
self.api_secret = Some(api_secret);
self
}

/// Finalize the configuration into an [`IrohServicesPreset`].
pub fn build(self) -> IrohServicesPreset {
IrohServicesPreset {
secret_key: self.secret_key,
relays: self.relays,
api_secret: self.api_secret,
}
}
}
Loading