feat(settings): per-service FIPS/Tor transport preference

Adds a user-configurable toggle for how each peer-to-peer service
reaches federated peers. Three options per service:

- Auto (default) — FIPS preferred, Tor fallback (current behavior).
- FIPS only — fail rather than fall through to Tor.
- Tor only — explicit opt-in to onion anonymity for that service.

Services covered (matching the UI rows):
- Federation — state sync, invites, peer notifications
- Peers — address/DID rotation broadcasts
- Peer Files — content catalog download/browse/preview
- Messaging — archipelago channel + mesh bridge
- Mesh File Sharing — content_ref blob fetches

Implementation:
- settings::transport — persisted struct + process-wide OnceLock handle
  (so deep call sites don't need data_dir threaded through signatures).
  On-disk file: <data_dir>/settings/transport_preferences.json; missing
  or corrupt → defaults (Auto everywhere).
- settings::transport::init() called from main.rs after config load.
- fips::dial::PeerRequest gains a .service(kind) builder; send_* checks
  the preference before choosing a transport. FIPS-only fails loudly
  when FIPS is unavailable (so users who pick it know when something
  falls back).
- Every FIPS-first migration site tags its PeerRequest with the
  matching PeerService so the toggle actually applies.
- transport.preferences + transport.set-preference RPCs added; wired
  into the dispatcher.
- neode-ui/src/views/settings/TransportPrefsCard.vue — standalone card
  with a 5-row Auto/FIPS/Tor tri-state. Not wired into Settings.vue —
  the user places components themselves (see feedback_ui_entry_points).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-04-19 01:44:41 -04:00
co-authored by Claude Opus 4.7
parent dbd19006f2
commit 8b88c45262
16 changed files with 478 additions and 4 deletions
+11
View File
@@ -56,6 +56,7 @@ mod rate_limit;
pub mod seed;
mod server;
mod session;
mod settings;
mod state;
mod streaming;
mod totp;
@@ -87,6 +88,16 @@ async fn main() -> Result<()> {
let config = Config::load().await?;
info!("📁 Data directory: {}", config.data_dir.display());
// Load user transport preferences so peer-to-peer call sites can
// consult them from any module without threading a handle through
// deep async chains. Missing/corrupt file → default (Auto everywhere).
if let Err(e) = settings::transport::init(&config.data_dir).await {
tracing::warn!(
"Failed to initialise transport preferences: {} — using defaults",
e
);
}
// Write PID marker early so we can detect crashes on next startup
crash_recovery::write_pid_marker(&config.data_dir).await?;