diff --git a/.gitignore b/.gitignore index 3e8e408..45384b2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ target/ .claude/ +.DS_Store diff --git a/varde-daemon/src/config.rs b/varde-daemon/src/config.rs index a43c004..e72d7f9 100644 --- a/varde-daemon/src/config.rs +++ b/varde-daemon/src/config.rs @@ -24,6 +24,12 @@ pub struct Config { /// Whether any WAN (non-link-local) upload is permitted. Default off: /// LAN-only posture with zero WAN upload. pub wan_upload: bool, + /// A self-hosted iroh relay URL for NAT traversal. When set, the + /// endpoint uses this relay (`RelayMode::Custom`) instead of n0's + /// or none — the sovereign hole-punch coordinator an embedder runs + /// on its own infrastructure. Takes precedence over `wan_upload`'s + /// n0 defaults. `None` keeps the LAN-only / n0 behaviour. + pub relay_url: Option, /// Interval of the store's built-in garbage collector in seconds. /// Since iroh-blobs 0.103 there is no on-demand gc; unpinned blobs /// are swept by this loop. @@ -41,6 +47,7 @@ struct FileConfig { max_download_bytes_per_sec: Option, discovery: Option, wan_upload: Option, + relay_url: Option, gc_interval_secs: Option, } @@ -176,6 +183,10 @@ impl Config { wan_upload: env_bool("VARDE_WAN_UPLOAD")? .or(file.wan_upload) .unwrap_or(false), + relay_url: std::env::var("VARDE_RELAY_URL") + .ok() + .filter(|s| !s.is_empty()) + .or(file.relay_url), gc_interval_secs: env_u64("VARDE_GC_INTERVAL")? .or(file.gc_interval_secs) .unwrap_or(300), diff --git a/varde-daemon/src/transfer.rs b/varde-daemon/src/transfer.rs index 9160fb9..9ac3d21 100644 --- a/varde-daemon/src/transfer.rs +++ b/varde-daemon/src/transfer.rs @@ -62,10 +62,19 @@ impl Transfer { metered: MeteredState, ) -> Result { let secret = load_or_create_secret(&config.store_dir.join("secret.key"))?; - // With wan_upload the n0 defaults apply (their relays and DNS - // lookup, matching the pre-1.0 default relay mode); otherwise the - // endpoint gets no external services at all. - let builder = if config.wan_upload { + // A self-hosted relay (relay_url) is the sovereign hole-punch + // path and takes precedence: the endpoint coordinates through + // the operator's own iroh relay, never n0's. Failing that, + // wan_upload opts into n0's relays + DNS; otherwise the endpoint + // gets no external services at all (LAN-only). + let builder = if let Some(url) = &config.relay_url { + let relay_url: iroh::RelayUrl = url + .parse() + .with_context(|| format!("parsing relay_url {url:?}"))?; + let relay_map = iroh::RelayMap::from(relay_url); + Endpoint::builder(iroh::endpoint::presets::Minimal) + .relay_mode(RelayMode::Custom(relay_map)) + } else if config.wan_upload { Endpoint::builder(iroh::endpoint::presets::N0) } else { Endpoint::builder(iroh::endpoint::presets::Minimal).relay_mode(RelayMode::Disabled)