security: parameterize node addresses; drop dead APP_URLS config
Demo images / Build & push demo images (push) Failing after 2m16s

Keeps the dev and test tooling an outside contributor would want, and takes
our node addresses out of it.

Scripts that silently defaulted to one of our nodes now require an explicit
host and exit 2 without one: smoke-test.sh, trust-archipelago-cert.sh,
dev-container-test.sh (which also derives its RPC and health URLs from the
SSH target instead of a second hardcoded copy), and image-recipe/dev-branding.sh.
A default that points at a machine the user does not own is worse than no
default: it fails confusingly, or reaches a stranger's device.

Usage examples, mock data and test fixtures move to the RFC 5737
documentation range (192.0.2.0/24). CGNAT test values stay inside
100.64.0.0/10 so the range-check semantics they exercise still hold, and
192.168.1.0/.1/.254 are left alone — those are gateway logic and UI
placeholders, not our addresses.

Playwright and the perf spec defaulted their baseURL to one of our nodes;
they now default to localhost:8100, the local dev server.

Removed neode-ui APP_URLS entirely. It is dead code — exported, never
imported — and it pinned fedimint's *prod* launch URL to 192.168.1.228:8175.
Had anything consumed it, every user's node would have tried to reach an
address that on their LAN is either nothing or someone else's machine.
Deleting beats sanitizing dead config.

Verified: frontend 868/868 vitest across 108 files; archipelago-container
75/75; mesh tests 9/9; audit-secrets 5/5. Zero node addresses and zero node
names remain in tracked files.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-08-07 10:53:25 -04:00
co-authored by Claude Opus 5
parent 6ba0599639
commit b2c7592840
49 changed files with 154 additions and 174 deletions
+1 -1
View File
@@ -219,7 +219,7 @@ impl RpcHandler {
anyhow::bail!("npub must be bech32 (npub1...)");
}
if !anchor.address.contains(':') {
anyhow::bail!("address must be host:port (e.g. 192.168.1.116:8668)");
anyhow::bail!("address must be host:port (e.g. 192.0.2.12:8668)");
}
let list = fips::anchors::add(&self.config.data_dir, anchor.clone()).await?;
// Push just the newly-added anchor into the running daemon so
+2 -2
View File
@@ -317,10 +317,10 @@ mod client_ip_tests {
#[test]
fn loopback_connection_trusts_forwarded_header() {
// nginx on loopback forwards the real client IP — use it.
let parts = parts_with(Some("127.0.0.1:44412"), Some("192.168.1.50"));
let parts = parts_with(Some("127.0.0.1:44412"), Some("192.0.2.50"));
assert_eq!(
extract_client_ip(&parts),
"192.168.1.50".parse::<IpAddr>().unwrap()
"192.0.2.50".parse::<IpAddr>().unwrap()
);
}
@@ -725,7 +725,7 @@ async fn seed_assist_pipeline(storage: &std::path::Path, claude_entity: Option<&
/// HA's zeroconf discovery stores a satellite as a fixed LAN IP. DHCP
/// renumbering — or the whole node moving to a different network — strands
/// the entry and the speaker silently drops (a test node 2026-07-23: entry
/// pinned to 192.168.1.241 while the LAN had become 192.168.63.0/24). HA
/// pinned to 192.0.2.16 while the LAN had become 192.168.63.0/24). HA
/// never re-resolves on its own. This keeper probes each satellite entry and,
/// when one stops answering, sweeps the node's local /24s for the same
/// Wyoming port and rewrites the entry to the address that answers.
+2 -2
View File
@@ -411,7 +411,7 @@ mod tests {
bind_host = "127.0.0.1"
bind_port = 9999
log_level = "debug"
host_ip = "192.168.1.100"
host_ip = "192.0.2.100"
dev_mode = true
container_runtime = "Podman"
port_offset = 20000
@@ -425,7 +425,7 @@ mod tests {
assert_eq!(config.bind_host, "127.0.0.1");
assert_eq!(config.bind_port, 9999);
assert_eq!(config.log_level, "debug");
assert_eq!(config.host_ip, "192.168.1.100");
assert_eq!(config.host_ip, "192.0.2.100");
assert!(config.dev_mode);
assert_eq!(config.port_offset, 20000);
assert!(!config.nostr_discovery_enabled);
+1 -1
View File
@@ -123,7 +123,7 @@ pub fn default_public_anchors() -> Vec<SeedAnchor> {
pub struct SeedAnchor {
/// Bech32 `npub1...` of the anchor's FIPS identity.
pub npub: String,
/// Directly-dialable transport address, e.g. `192.168.1.116:8668`.
/// Directly-dialable transport address, e.g. `192.0.2.12:8668`.
pub address: String,
/// Transport to use — almost always `"udp"`.
#[serde(default = "default_transport")]
+4 -4
View File
@@ -110,8 +110,8 @@ mod tests {
#[test]
fn route_src_wins() {
let route = "default via 192.168.1.254 dev wlp3s0 proto dhcp src 192.168.1.116 metric 600";
assert_eq!(parse_route_src(route).as_deref(), Some("192.168.1.116"));
let route = "default via 192.168.1.254 dev wlp3s0 proto dhcp src 192.0.2.12 metric 600";
assert_eq!(parse_route_src(route).as_deref(), Some("192.0.2.12"));
}
#[test]
@@ -123,8 +123,8 @@ mod tests {
#[test]
fn addr_inet_strips_prefix() {
let out = "3: wlp3s0 inet 192.168.1.65/24 brd 192.168.1.255 scope global dynamic noprefixroute wlp3s0\\ valid_lft 85328sec preferred_lft 85328sec";
assert_eq!(parse_addr_inet(out).as_deref(), Some("192.168.1.65"));
let out = "3: wlp3s0 inet 192.0.2.17/24 brd 192.0.2.255 scope global dynamic noprefixroute wlp3s0\\ valid_lft 85328sec preferred_lft 85328sec";
assert_eq!(parse_addr_inet(out).as_deref(), Some("192.0.2.17"));
}
#[test]
+2 -2
View File
@@ -522,7 +522,7 @@ mod tests {
trust_level: None,
source: None,
mesh_contact_id: Some(42),
lan_address: Some("192.168.1.100:5678".to_string()),
lan_address: Some("192.0.2.100:5678".to_string()),
fips_npub: None,
onion_address: Some("abc123.onion".to_string()),
last_mesh: None,
@@ -536,7 +536,7 @@ mod tests {
);
assert_eq!(
peer.address_for(TransportKind::Lan),
Some("192.168.1.100:5678".to_string())
Some("192.0.2.100:5678".to_string())
);
assert_eq!(
peer.address_for(TransportKind::Tor),