feat(content): route peer content fetch via FIPS first

All four content-over-peer handlers prefer FIPS when the peer is in
our federation and has advertised a FIPS npub; fall back to Tor
otherwise (unknown peers, FIPS daemon down, transient failure).

- content.handle_content_download_peer / _paid: DID-authenticated
  fetch, payment token header threaded through both transports.
- content.handle_content_browse_peer / _preview: no DID header by
  design (anonymous browse) — still benefits from FIPS when the
  peer happens to be federated.
- federation::fips_npub_for_onion: storage helper that looks up a
  peer's FIPS npub from the federation nodes file given their onion
  address. Suffix-tolerant (`abc` matches `abc.onion`).

Preserves the Tor-only path for truly unknown peers: PeerRequest
returns Err from the Tor branch instead of silently succeeding,
matching the previous behavior when the peer was unreachable.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-04-19 01:29:13 -04:00
co-authored by Claude Opus 4.7
parent 1fdb5e5cf2
commit ba825c13a5
3 changed files with 62 additions and 67 deletions
+4 -1
View File
@@ -12,6 +12,9 @@ mod types;
// Re-export all public items so `crate::federation::*` continues to work.
pub use invites::{accept_invite, create_invite};
pub use storage::{add_node, load_nodes, remove_node, save_nodes, set_trust_level, update_node};
pub use storage::{
add_node, fips_npub_for_onion, load_nodes, remove_node, save_nodes, set_trust_level,
update_node,
};
pub use sync::{build_local_state, deploy_to_peer, sync_with_peer};
pub use types::{AppStatus, FederatedNode, NodeStateSnapshot, TrustLevel};
@@ -47,6 +47,19 @@ pub async fn load_nodes(data_dir: &Path) -> Result<Vec<FederatedNode>> {
Ok(file.nodes)
}
/// Look up a federated peer's FIPS npub given their onion address.
/// Returns `None` when the onion isn't in our federation list or the
/// peer hasn't advertised a FIPS key. Matching is suffix-tolerant so
/// callers can pass `abc` or `abc.onion` interchangeably.
pub async fn fips_npub_for_onion(data_dir: &Path, onion: &str) -> Option<String> {
let target = onion.trim_end_matches(".onion");
let nodes = load_nodes(data_dir).await.ok()?;
nodes
.iter()
.find(|n| n.onion.trim_end_matches(".onion") == target)
.and_then(|n| n.fips_npub.clone())
}
pub async fn save_nodes(data_dir: &Path, nodes: &[FederatedNode]) -> Result<()> {
let dir = ensure_dir(data_dir).await?;
let file = NodesFile {