Skip to main content

max / makenotwork

pom: honor an explicit scheme in the peer address The heartbeat loop hardcoded http://{address}, so an operator could not point pom at an https:// peer without a code change. peer_base_url now uses a scheme already present in the address and defaults to http:// otherwise — the tailnet deployment where WireGuard encrypts the hop and pom's own API binds plain HTTP. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-07-07 17:32 UTC
Commit: 276f3d15e0b01e6c808d7a71f9cbb1b12d8312d2
Parent: d1e1f6b
1 file changed, +24 insertions, -2 deletions
M pom/src/peer.rs +24 -2
@@ -36,6 +36,20 @@ async fn read_capped_json<T: serde::de::DeserializeOwned>(
36 36 serde_json::from_slice(&bytes).ok()
37 37 }
38 38
39 + /// Build the base URL for a peer's API from its configured address.
40 + ///
41 + /// Honors a scheme already present in the address (so an operator can set
42 + /// `https://host:port` when the hop crosses an untrusted network), and otherwise
43 + /// defaults to `http://` — the tailnet deployment where WireGuard already
44 + /// encrypts the transport, and pom's own API server binds plain HTTP.
45 + fn peer_base_url(address: &str) -> String {
46 + if address.contains("://") {
47 + address.trim_end_matches('/').to_string()
48 + } else {
49 + format!("http://{address}")
50 + }
51 + }
52 +
39 53 // --- Identity ---
40 54
41 55 /// Load or create a persistent instance ID (UUID v4).
@@ -216,6 +230,7 @@ async fn heartbeat_loop(
216 230 None => return,
217 231 }
218 232 };
233 + let base = peer_base_url(&address);
219 234
220 235 let client = reqwest::Client::builder()
221 236 .timeout(std::time::Duration::from_secs(10))
@@ -229,7 +244,7 @@ async fn heartbeat_loop(
229 244 }
230 245
231 246 let start = std::time::Instant::now();
232 - let mut req = client.get(format!("http://{address}/api/peer/info"));
247 + let mut req = client.get(format!("{base}/api/peer/info"));
233 248 if let Some(ref t) = auth_token {
234 249 req = req.bearer_auth(t);
235 250 }
@@ -247,7 +262,7 @@ async fn heartbeat_loop(
247 262 }
248 263
249 264 // Also fetch /api/peer/status for mesh aggregation
250 - let mut status_req = client.get(format!("http://{address}/api/peer/status"));
265 + let mut status_req = client.get(format!("{base}/api/peer/status"));
251 266 if let Some(ref t) = auth_token {
252 267 status_req = status_req.bearer_auth(t);
253 268 }
@@ -416,6 +431,13 @@ mod tests {
416 431 use super::*;
417 432
418 433 #[test]
434 + fn peer_base_url_honors_scheme() {
435 + assert_eq!(peer_base_url("host:9100"), "http://host:9100");
436 + assert_eq!(peer_base_url("https://host:9100"), "https://host:9100");
437 + assert_eq!(peer_base_url("https://host:9100/"), "https://host:9100");
438 + }
439 +
440 + #[test]
419 441 fn override_id_takes_precedence() {
420 442 let id = load_or_create_instance_id(Some("override-id")).unwrap();
421 443 assert_eq!(id, "override-id");