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
+16
View File
@@ -405,6 +405,13 @@ impl Node {
return;
};
// Refresh the runtime's outbound-admission view once per tick.
// The runtime task lives in a separate tokio context with no Node
// reference, so we publish current capacity state through a
// cheap atomic store. One-tick lag is acceptable: the inbound
// msg1 gate in handshake.rs remains the authoritative cap.
bootstrap.set_outbound_admission(self.outbound_admission_check());
if let Err(err) = self.refresh_overlay_advert(&bootstrap).await {
debug!(error = %err, "Failed to refresh local Nostr overlay advert");
}
@@ -412,6 +419,15 @@ impl Node {
for event in bootstrap.drain_events().await {
match event {
BootstrapEvent::Established { traversal } => {
if !self.outbound_admission_check() {
debug!(
peer_npub = %traversal.peer_npub,
peers = self.peers.len(),
max_peers = self.max_peers,
"Dropping established NAT traversal: at capacity"
);
continue;
}
let peer_npub = traversal.peer_npub.clone();
if let Ok(peer_identity) = PeerIdentity::from_npub(&peer_npub) {
let peer_addr = *peer_identity.node_addr();