feat(ui): direct-only mode setting (no relay fallback)
Some checks failed
Mirror to GitHub / mirror (push) Failing after 24s
Build Release Binaries / build-amd64 (push) Failing after 3m38s

New toggle in Settings → "Direct-only mode (no relay fallback)":
- Default: OFF (normal behavior, relay fallback on P2P failure)
- When ON: connect returns error if P2P fails, with full
  candidate_diags in the debug log showing why each candidate
  failed. Call never falls back to relay.

Useful for testing NAT traversal — you see the exact failure
reason instead of the call silently working through relay.

Wired end-to-end:
- Settings.directOnly persisted in localStorage
- Passed as directOnly param to Rust connect command
- connect:path_negotiated shows direct_only flag
- connect:direct_only_failed emits on failure with diags

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Siavash Sameni
2026-04-14 16:04:45 +04:00
parent 1de280fe04
commit 6c49d7436f
3 changed files with 31 additions and 0 deletions

View File

@@ -333,13 +333,19 @@ async fn connect(
// Phase 8 (Tailscale-inspired): peer's port-mapped external
// address from NAT-PMP/PCP/UPnP, carried in CallSetup.
peer_mapped_addr: Option<String>,
// Debug: when true, skip relay fallback entirely — the call
// fails if direct P2P doesn't connect. Useful for testing NAT
// traversal without the relay masking failures.
direct_only: Option<bool>,
) -> Result<String, String> {
let force_direct = direct_only.unwrap_or(false);
emit_call_debug(&app, "connect:start", serde_json::json!({
"relay": relay,
"room": room,
"peer_direct_addr": peer_direct_addr,
"peer_local_addrs": peer_local_addrs,
"peer_mapped_addr": peer_mapped_addr,
"direct_only": force_direct,
}));
let mut engine_lock = state.engine.lock().await;
if engine_lock.is_some() {
@@ -572,7 +578,20 @@ async fn connect(
"local_direct_ok": local_direct_ok,
"peer_direct_ok": peer_direct_ok,
"chosen_path": format!("{:?}", chosen_path),
"direct_only": force_direct,
}));
// direct_only mode: refuse relay fallback
if force_direct && !use_direct {
let reason = format!(
"direct_only: P2P failed (local_ok={local_direct_ok}, peer_ok={peer_direct_ok})"
);
emit_call_debug(&app, "connect:direct_only_failed", serde_json::json!({
"reason": reason,
"candidate_diags": race_result.candidate_diags,
}));
return Err(reason);
}
tracing::info!(
?chosen_path,
use_direct,