fix(federation): end the perpetual peer-joined "Invalid signature" storm
Root cause observed live 2026-08-16: onboarding/seed-restore rewrite identity/node_key on disk but server_info.pubkey is only seeded at boot, so until the next restart every peer-joined advertised the stale boot key while signing with the new seed-derived key — deterministically rejected by every receiver, once per 90s heal tick, forever. - seed.generate / seed.restore now refresh server_info.pubkey in the live snapshot immediately (mirrors the DID-rotation handler). - The 90s heal loop advertises the SAME key it signs with (disk identity, like federation sync already did) instead of the boot snapshot. - notify_join no longer logs "delivered" for an HTTP-200 JSON-RPC rejection; in-band errors are terminal (identical signed bytes can never succeed on retry). - The heal loop backs off per peer (doubling toward a daily re-assert) instead of re-notifying every 90s forever — Observer-held peers never appear in Trusted-only exported hints, so they_list_us could never become true for them. - Receiver now binds the DID to the advertised pubkey (the old check was self-referential) and logs malformed signatures distinctly from genuine mismatches. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
809f7649a4
commit
519fa68c72
@@ -326,6 +326,26 @@ pub(crate) async fn notify_join(
|
||||
.await;
|
||||
match res {
|
||||
Ok((resp, transport)) if resp.status().is_success() => {
|
||||
// A JSON-RPC-level rejection still arrives as HTTP 200
|
||||
// (the RPC layer returns errors in-band), so checking the
|
||||
// status alone logged "delivered" for calls the peer had
|
||||
// just rejected. Read the body: an in-band error is
|
||||
// terminal — the signed payload is deterministic, so
|
||||
// retrying identical bytes can never succeed.
|
||||
let body = resp.text().await.unwrap_or_default();
|
||||
let rpc_err = serde_json::from_str::<serde_json::Value>(&body)
|
||||
.ok()
|
||||
.and_then(|v| v.get("error").cloned())
|
||||
.filter(|e| !e.is_null());
|
||||
if let Some(err) = rpc_err {
|
||||
tracing::warn!(
|
||||
attempt,
|
||||
transport = %transport,
|
||||
error = %err,
|
||||
"peer-joined notification rejected by peer — giving up (retrying identical payload cannot succeed)"
|
||||
);
|
||||
return;
|
||||
}
|
||||
tracing::info!(
|
||||
attempt,
|
||||
transport = %transport,
|
||||
|
||||
Reference in New Issue
Block a user