Merge maint into master (outbound admission gate + mesh-size parent skip)

Brings two structural fixes landed on maint:
- compute_mesh_size: explicit parent skip in the children loop, so the
  disjoint-subtree invariant no longer depends on peer_declaration cache
  freshness.
- max_peers: outbound connection-initiation gated on the cap (auto-reconnect
  retries, Nostr-mediated discovery established adoption, and both sides
  of the NAT-traversal punch sequence). Inbound msg1 admission gate
  unchanged.
This commit is contained in:
Johnathan Corgan
2026-05-26 17:31:47 +00:00
7 changed files with 358 additions and 0 deletions
+15
View File
@@ -1239,6 +1239,9 @@ impl Node {
// Children's filters: each child's subtree is disjoint
for (peer_addr, peer) in &self.peers {
if peer_addr == &parent_id {
continue;
}
if let Some(decl) = self.tree_state.peer_declaration(peer_addr)
&& *decl.parent_id() == my_addr
{
@@ -1398,6 +1401,18 @@ impl Node {
self.max_peers = max;
}
/// Returns false when we are at or above the configured `max_peers`
/// cap, suppressing outbound connection-initiation. `max_peers == 0`
/// is the "no cap" sentinel and always returns true. The inbound
/// msg1 gate in `handshake.rs` is the authoritative cap; this helper
/// keeps the four outbound initiation paths (auto-reconnect retries,
/// Nostr-discovery `Established` adoption, and both sides of the
/// Nostr-mediated NAT-traversal punch) from doing pointless work
/// when saturated.
pub(crate) fn outbound_admission_check(&self) -> bool {
self.max_peers == 0 || self.peers.len() < self.max_peers
}
/// Set the maximum number of links.
pub fn set_max_links(&mut self, max: usize) {
self.max_links = max;