feat(fips): A3.10 — last-known-good endpoint fallback for direct peering

New fips/endpoints.rs: an npub-keyed store (<data_dir>/fips-endpoints.json)
of every endpoint a peer was last seen connected at (fipsctl show peers
transport_addr/transport_type — covers LAN, Tailscale, and WAN alike),
refreshed each anchor tick, 30-day retention.

The anchor tick now escalates LAN → last-known-good → anchor tree: any
federation peer with a fips npub that is neither currently connected nor
covered by a live LAN direct entry gets its last-known-good endpoint
re-dialed (idempotent fipsctl connect, bounded by apply()'s per-connect
cap). This productizes the hand-applied .116↔.198 Tailscale fix of
2026-07-20 and closes RC2's "no endpoint fallback" gap.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-07-28 07:33:32 -04:00
co-authored by Claude Fable 5
parent 43130f33f0
commit c0a5635ba3
4 changed files with 288 additions and 0 deletions
+36
View File
@@ -786,6 +786,42 @@ impl Server {
if !direct.is_empty() {
let _ = crate::fips::anchors::apply(&direct).await;
}
// A3.10 — endpoint fallback for direct peering. Record
// where currently-connected peers actually are (their
// transport_addr covers LAN, Tailscale, and WAN alike),
// then re-dial the last-known-good endpoint of every
// federation peer whose live paths are gone: not
// connected now, no LAN direct entry this tick. Escala-
// tion order is LAN → last-known-good → anchor tree;
// a stale candidate costs one bounded failed dial.
let connected =
crate::fips::service::connected_peer_endpoints().await;
let known = crate::fips::endpoints::record_connected(
&data_dir, &connected,
)
.await;
let wanted: Vec<String> = reg
.all_peers()
.await
.iter()
.filter_map(|p| p.fips_npub.clone())
.collect();
let connected_npubs: Vec<String> =
connected.iter().map(|c| c.npub.clone()).collect();
let fallback = crate::fips::endpoints::fallback_anchors(
&known,
&wanted,
&connected_npubs,
&direct,
);
if !fallback.is_empty() {
tracing::info!(
count = fallback.len(),
"dialing last-known-good endpoints for disconnected federation peers"
);
let _ = crate::fips::anchors::apply(&fallback).await;
}
}
let next = if daemon_restarting && fast_retries < MAX_FAST_RETRIES {