polish: professionalize all user-facing log output and UX

- Unified log prefix to [ostp] across all modules (was [OSTP Core],
  [ostp-server], [ostp-client], [client], [bridge])
- Removed informal/casual phrasing from all user-visible messages
- Startup messages are clean and concise (mode, server, status)
- Error messages are actionable without being alarming
- Essential server logs (client connect/disconnect) always visible
- Essential client logs (connection status, errors) always visible
- TUN tunnel messages consistent across Windows and Linux
- Removed noisy eprintln from UDP reader hot path
- Status format: [ostp] Status: Connected (rtt=12.3ms)
This commit is contained in:
ospab 2026-05-17 03:26:15 +03:00
parent 7424ccc0ff
commit 8eb3fc72cb
6 changed files with 64 additions and 76 deletions

View File

@ -108,9 +108,9 @@ impl Bridge {
let mut keepalive_tick = tokio::time::interval(Duration::from_secs(5)); let mut keepalive_tick = tokio::time::interval(Duration::from_secs(5));
let mut retransmit_tick = tokio::time::interval(Duration::from_millis(50)); let mut retransmit_tick = tokio::time::interval(Duration::from_millis(50));
let init_msg = if self.mode == "tun" { let init_msg = if self.mode == "tun" {
"Bridge & TUN Tunnel Manager initialized".to_string() "Bridge initialized (TUN mode)".to_string()
} else { } else {
"Bridge & SOCKS5 Proxy initialized".to_string() "Bridge initialized (proxy mode)".to_string()
}; };
tx.send(UiEvent::Log(init_msg)).await.ok(); tx.send(UiEvent::Log(init_msg)).await.ok();
@ -203,7 +203,7 @@ impl Bridge {
} }
} }
None => { None => {
let _ = tx.send(UiEvent::Log("UDP reader channel closed".to_string())).await; let _ = tx.send(UiEvent::Log("UDP channel closed, resetting connection".to_string())).await;
self.running = false; self.running = false;
crate::sysproxy::disable_windows_proxy(); crate::sysproxy::disable_windows_proxy();
sessions_opt = None; sessions_opt = None;
@ -226,7 +226,7 @@ impl Bridge {
stream_map.clear(); stream_map.clear();
self.reset_proxy_streams(&tx, &proxy_tx, "manual stop"); self.reset_proxy_streams(&tx, &proxy_tx, "manual stop");
tx.send(UiEvent::TunnelStopped).await.ok(); tx.send(UiEvent::TunnelStopped).await.ok();
let stop_msg = if self.mode == "tun" { "TUN Tunnel stopped" } else { "Bridge stopped" }; let stop_msg = if self.mode == "tun" { "TUN tunnel stopped" } else { "Bridge stopped" };
tx.send(UiEvent::Log(stop_msg.to_string())).await.ok(); tx.send(UiEvent::Log(stop_msg.to_string())).await.ok();
} else { } else {
tx.send(UiEvent::Log("Connecting to remote server...".to_string())).await.ok(); tx.send(UiEvent::Log("Connecting to remote server...".to_string())).await.ok();
@ -263,12 +263,10 @@ impl Bridge {
Bytes::copy_from_slice(&buf[..n]) Bytes::copy_from_slice(&buf[..n])
}; };
if udp_tx_clone.send((idx, inbound)).await.is_err() { if udp_tx_clone.send((idx, inbound)).await.is_err() {
eprintln!("[bridge] UDP receiver task exiting: bridge channel full or closed");
break; break;
} }
} }
Err(e) => { Err(_) => {
eprintln!("[bridge] UDP socket recv error: {e}");
break; break;
} }
} }
@ -309,7 +307,7 @@ impl Bridge {
throughput_bps: 0, throughput_bps: 0,
}).await.ok(); }).await.ok();
self.metrics.connection_state.store(2, Ordering::Relaxed); self.metrics.connection_state.store(2, Ordering::Relaxed);
let start_msg = if self.mode == "tun" { "TUN Tunnel established" } else { "Connection established" }; let start_msg = if self.mode == "tun" { "TUN tunnel established" } else { "Connection established" };
tx.send(UiEvent::Log(start_msg.to_string())).await.ok(); tx.send(UiEvent::Log(start_msg.to_string())).await.ok();
} }
} }
@ -626,11 +624,11 @@ impl Bridge {
} else { } else {
format!("{}:3478", self.turn_server) format!("{}:3478", self.turn_server)
}; };
tx.send(UiEvent::Log(format!("TURN: Allocating relay via {}", turn_addr))).await.ok(); tx.send(UiEvent::Log(format!("Allocating TURN relay via {}", turn_addr))).await.ok();
match perform_turn_allocation(&socket, &turn_addr, &self.turn_username, &self.turn_password, &self.server_addr).await { match perform_turn_allocation(&socket, &turn_addr, &self.turn_username, &self.turn_password, &self.server_addr).await {
Ok(relay_addr) => { Ok(relay_addr) => {
tx.send(UiEvent::Log(format!("TURN: Relay allocated. Traffic tunnelled via {}", relay_addr))).await.ok(); tx.send(UiEvent::Log(format!("TURN relay allocated ({})", relay_addr))).await.ok();
// Re-connect the UDP socket to the TURN server so all sends go through it. // Re-connect the UDP socket to the TURN server so all sends go through it.
// The TURN server forwards ChannelData to the OSTP server transparently. // The TURN server forwards ChannelData to the OSTP server transparently.
socket socket
@ -639,7 +637,7 @@ impl Bridge {
.with_context(|| format!("failed to re-connect to TURN {}", turn_addr))?; .with_context(|| format!("failed to re-connect to TURN {}", turn_addr))?;
} }
Err(e) => { Err(e) => {
tx.send(UiEvent::Log(format!("TURN allocation failed: {e}. Falling back to direct UDP."))).await.ok(); tx.send(UiEvent::Log(format!("TURN allocation failed: {}. Using direct UDP.", e))).await.ok();
socket socket
.connect(&self.server_addr) .connect(&self.server_addr)
.await .await
@ -647,7 +645,7 @@ impl Bridge {
} }
} }
} else { } else {
tx.send(UiEvent::Log(format!("Connected UDP directly to {}", self.server_addr))).await.ok(); tx.send(UiEvent::Log(format!("Connected to {}", self.server_addr))).await.ok();
socket socket
.connect(&self.server_addr) .connect(&self.server_addr)
.await .await

View File

@ -110,7 +110,7 @@ fn relaunch_as_admin() -> Result<()> {
pub async fn run_client(config: crate::config::ClientConfig) -> Result<()> { pub async fn run_client(config: crate::config::ClientConfig) -> Result<()> {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
if config.mode == "tun" && !is_admin() { if config.mode == "tun" && !is_admin() {
println!("[ostp-client] TUN mode requires Administrator privileges. Relaunching executable as Admin..."); println!("[ostp] TUN mode requires administrator privileges. Relaunching...");
relaunch_as_admin()?; relaunch_as_admin()?;
} }
@ -150,7 +150,7 @@ pub async fn run_client_core(
log_to_core_file(&format!("[core] Starting run_client_core in mode: {}", config.mode)); log_to_core_file(&format!("[core] Starting run_client_core in mode: {}", config.mode));
if config.mode == "tun" && !config.exclusions.processes.is_empty() { if config.mode == "tun" && !config.exclusions.processes.is_empty() {
println!("[ostp-client] WARNING: process exclusions are not supported in the current TUN implementation"); println!("[ostp] Process exclusions are not supported in TUN mode");
} }
let (proxy_events_tx, proxy_events_rx) = mpsc::channel(256); let (proxy_events_tx, proxy_events_rx) = mpsc::channel(256);
@ -177,25 +177,25 @@ pub async fn run_client_core(
match msg { match msg {
crate::app::UiEvent::Log(text) => { crate::app::UiEvent::Log(text) => {
if debug_enabled || is_essential_log(&text) { if debug_enabled || is_essential_log(&text) {
log_to_core_file(&format!("[client] {text}")); log_to_core_file(&format!("[ostp] {text}"));
println!("[client] {text}"); println!("[ostp] {text}");
} }
} }
crate::app::UiEvent::Metrics { status, rtt_ms, .. } => { crate::app::UiEvent::Metrics { status, rtt_ms, .. } => {
let status_str = status.as_str().to_string(); let status_str = status.as_str().to_string();
if last_status != Some(status_str.clone()) { if last_status != Some(status_str.clone()) {
last_status = Some(status_str.clone()); last_status = Some(status_str.clone());
println!("[client] status={status_str} rtt_ms={:.1}", rtt_ms); println!("[ostp] Status: {} (rtt={:.1}ms)", status_str, rtt_ms);
} }
} }
crate::app::UiEvent::Traffic { .. } => {} crate::app::UiEvent::Traffic { .. } => {}
crate::app::UiEvent::ProfileChanged(profile) => { crate::app::UiEvent::ProfileChanged(profile) => {
if debug_enabled { if debug_enabled {
println!("[client] profile={profile:?}"); println!("[ostp] Obfuscation profile: {profile:?}");
} }
} }
crate::app::UiEvent::TunnelStopped => { crate::app::UiEvent::TunnelStopped => {
println!("[client] Connection lost or failed. Reconnecting in 5s..."); println!("[ostp] Connection interrupted. Reconnecting in 5 seconds...");
let cmd_tx_inner = cmd_tx_clone.clone(); let cmd_tx_inner = cmd_tx_clone.clone();
tokio::spawn(async move { tokio::spawn(async move {
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await; tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
@ -280,14 +280,15 @@ fn is_essential_log(text: &str) -> bool {
matches!( matches!(
text, text,
"Connection established" "Connection established"
| "TUN Tunnel established" | "TUN tunnel established"
| "TUN tunnel stopped"
| "Bridge stopped" | "Bridge stopped"
| "TUN Tunnel stopped"
| "Runtime config reloaded" | "Runtime config reloaded"
| "Connecting to remote server..." | "Connecting to remote server..."
) || text.starts_with("Connected UDP directly to ") ) || text.starts_with("Connected to ")
|| text.starts_with("TURN: Relay allocated") || text.starts_with("TURN relay allocated")
|| text.starts_with("TURN allocation failed") || text.starts_with("TURN allocation failed")
|| text.starts_with("Allocating TURN relay")
|| text.starts_with("Connection failed:") || text.starts_with("Connection failed:")
|| text.starts_with("Connection lost") || text.starts_with("Connection lost")
|| text.starts_with("Protocol tick fatal error") || text.starts_with("Protocol tick fatal error")

View File

@ -43,7 +43,7 @@ pub async fn run_linux_tunnel(
) -> Result<()> { ) -> Result<()> {
let debug = config.debug; let debug = config.debug;
if debug { if debug {
println!("[ostp-client] Starting Linux TUN handler initialization..."); println!("[ostp] Initializing TUN tunnel...");
} }
let exe = std::env::current_exe()?; let exe = std::env::current_exe()?;
@ -102,7 +102,7 @@ pub async fn run_linux_tunnel(
let server_ip_str = server_ip.to_string(); let server_ip_str = server_ip.to_string();
if debug { if debug {
println!("[ostp-client] Resolved remote server IP: {}", server_ip_str); println!("[ostp-client] Resolved server IP: {}", server_ip_str);
} }
// 3. Detect current default gateway and interface // 3. Detect current default gateway and interface
@ -132,7 +132,7 @@ pub async fn run_linux_tunnel(
} }
if debug { if debug {
println!("[ostp-client] Physical route anchor: gateway={} interface={}", default_gw, default_if); println!("[ostp-client] Default route: gateway={} interface={}", default_gw, default_if);
} }
// 4. Setup commands (Using standard /1 routing trick for fail-proof overriding) // 4. Setup commands (Using standard /1 routing trick for fail-proof overriding)
@ -187,7 +187,7 @@ pub async fn run_linux_tunnel(
child: None, child: None,
}; };
println!("[client] TUN Tunnel established, Linux traffic is now routing through OSTP."); println!("[ostp] TUN tunnel active. All traffic is routed through OSTP.");
if debug { if debug {
let stdout = child.stdout.take().unwrap(); let stdout = child.stdout.take().unwrap();
@ -213,12 +213,12 @@ pub async fn run_linux_tunnel(
// 6. Wait for shutdown signal // 6. Wait for shutdown signal
let _ = shutdown.changed().await; let _ = shutdown.changed().await;
println!("[client] Deactivating TUN tunnel and restoring Linux network topology..."); println!("[ostp] Deactivating TUN tunnel...");
// Drop guard runs cleanup automatically // Drop guard runs cleanup automatically
drop(_guard); drop(_guard);
println!("[client] Linux TUN Tunnel stopped."); println!("[ostp] TUN tunnel stopped.");
Ok(()) Ok(())
} }

View File

@ -37,7 +37,7 @@ pub async fn run_wintun_tunnel(
let debug = config.debug; let debug = config.debug;
if debug { if debug {
println!("[ostp-client] Initializing high-performance TUN tunnel via tun2socks..."); println!("[ostp] Initializing TUN tunnel...");
} }
let exe = std::env::current_exe()?; let exe = std::env::current_exe()?;
@ -64,12 +64,12 @@ pub async fn run_wintun_tunnel(
let server_ip_str = server_ip.to_string(); let server_ip_str = server_ip.to_string();
if debug { if debug {
println!("[ostp-client] Resolved remote server IP: {}", server_ip_str); println!("[ostp-client] Resolved server IP: {}", server_ip_str);
} }
// 3. Run PowerShell script to configure system routes // 3. Run PowerShell script to configure system routes
if debug { if debug {
println!("[ostp-client] Injecting system routing tables and excluding remote proxy..."); println!("[ostp-client] Configuring system routes...");
} }
let current_exe = std::env::current_exe()?.to_string_lossy().into_owned(); let current_exe = std::env::current_exe()?.to_string_lossy().into_owned();
@ -111,7 +111,7 @@ pub async fn run_wintun_tunnel(
let proxy_url = format!("http://{}", config.local_proxy.bind_addr); let proxy_url = format!("http://{}", config.local_proxy.bind_addr);
if debug { if debug {
println!("[ostp-client] Spawning tun2socks daemon pointing to {}", proxy_url); println!("[ostp-client] Starting tun2socks (proxy={})", proxy_url);
} }
// Spawning buffer to allow local proxy listener to finish binding to local address // Spawning buffer to allow local proxy listener to finish binding to local address
@ -139,7 +139,7 @@ pub async fn run_wintun_tunnel(
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await; tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
if debug { if debug {
println!("[ostp-client] Applying network configurations onto 'ostp_tun' interface..."); println!("[ostp-client] Applying network configuration...");
} }
let mut net_setup = String::from("\ let mut net_setup = String::from("\
@ -150,7 +150,7 @@ pub async fn run_wintun_tunnel(
if let Some(ref dns) = config.dns_server { if let Some(ref dns) = config.dns_server {
if !dns.is_empty() { if !dns.is_empty() {
if debug { if debug {
println!("[ostp-client] Applying custom DNS server: {}", dns); println!("[ostp-client] DNS server: {}", dns);
} }
net_setup.push_str(&format!("netsh interface ipv4 set dnsservers name=\"ostp_tun\" static {} primary\n", dns)); net_setup.push_str(&format!("netsh interface ipv4 set dnsservers name=\"ostp_tun\" static {} primary\n", dns));
} }
@ -161,7 +161,7 @@ pub async fn run_wintun_tunnel(
.args(["-Command", &net_setup]) .args(["-Command", &net_setup])
.output()?; .output()?;
println!("[client] TUN Tunnel established, internet traffic is now routing through OSTP."); println!("[ostp] TUN tunnel active. All traffic is routed through OSTP.");
// 6. Spawn thread to keep logging tun2socks output if in debug mode // 6. Spawn thread to keep logging tun2socks output if in debug mode
let mut stdout = child.stdout.take(); let mut stdout = child.stdout.take();
@ -192,12 +192,12 @@ pub async fn run_wintun_tunnel(
// 7. Wait for shutdown signal // 7. Wait for shutdown signal
let _ = shutdown.changed().await; let _ = shutdown.changed().await;
println!("[client] Deactivating TUN tunnel and restoring system network topology..."); println!("[ostp] Deactivating TUN tunnel...");
// Drop guard runs cleanup automatically // Drop guard runs cleanup automatically
drop(_guard); drop(_guard);
println!("[client] TUN Tunnel stopped."); println!("[ostp] TUN tunnel stopped.");
Ok(()) Ok(())
} }

View File

@ -152,23 +152,21 @@ pub async fn run_server(
while let Some(ev) = ui_event_rx.recv().await { while let Some(ev) = ui_event_rx.recv().await {
match ev { match ev {
UiEvent::Log(msg) => { UiEvent::Log(msg) => {
if debug // Essential logs always visible; debug logs gated behind flag
|| msg.starts_with("Listening on ") let is_essential = msg.starts_with("Client ")
|| msg.starts_with("Hot-reloaded ") || msg.starts_with("Listening")
|| msg.starts_with("Client ") || msg.starts_with("Shutdown")
|| msg.starts_with("Cleaning up resources") || msg.starts_with("Session ");
{ if debug || is_essential {
println!("[ostp-server] {msg}"); println!("[ostp] {msg}");
} }
} }
UiEvent::KeyCreated { key } => { UiEvent::KeyCreated { key } => {
if debug { println!("[ostp] Access key created: {key}");
println!("[ostp-server] New access key created: {key}");
}
} }
UiEvent::UnauthorizedProbe { peer, bytes } => { UiEvent::UnauthorizedProbe { peer, bytes } => {
if debug { if debug {
println!("[ostp-server] WARNING: unauthorized probe from {peer} ({bytes} bytes)"); println!("[ostp] Unauthorized probe from {peer} ({bytes} bytes)");
} }
} }
UiEvent::PeerSeen { .. } => {} UiEvent::PeerSeen { .. } => {}
@ -177,15 +175,15 @@ pub async fn run_server(
} }
}); });
println!("[ostp-server] Listening on {bind_addr}"); println!("[ostp] Listening on {bind_addr}");
tokio::select! { tokio::select! {
res = run_server_loop(socket, dispatcher, max_datagram_size, ui_cmd_rx, ui_event_tx, shared_keys, outbound, debug) => { res = run_server_loop(socket, dispatcher, max_datagram_size, ui_cmd_rx, ui_event_tx, shared_keys, outbound, debug) => {
if let Err(e) = res { if let Err(e) = res {
eprintln!("[ostp-server] error: {e}"); eprintln!("[ostp] Server error: {e}");
} }
} }
_ = wait_for_shutdown_signal() => { _ = wait_for_shutdown_signal() => {
println!("[ostp-server] shutdown signal received"); println!("[ostp] Shutdown signal received");
} }
} }
@ -361,7 +359,7 @@ async fn run_server_loop(
let _ = socket.send_to(&frame, peer_addr).await?; let _ = socket.send_to(&frame, peer_addr).await?;
} }
for sid in dropped_sessions { for sid in dropped_sessions {
let _ = ui_event_tx.send(UiEvent::Log(format!("Cleaning up resources for expired session {sid}"))); let _ = ui_event_tx.send(UiEvent::Log(format!("Session {sid} expired, releasing resources")));
let mut streams_to_cancel = Vec::new(); let mut streams_to_cancel = Vec::new();
for (&(session_id, stream_id), _) in &remotes { for (&(session_id, stream_id), _) in &remotes {
if session_id == sid { if session_id == sid {

View File

@ -203,10 +203,9 @@ struct MuxConfig {
async fn main() -> Result<()> { async fn main() -> Result<()> {
let res = run_app().await; let res = run_app().await;
if let Err(e) = res { if let Err(e) = res {
eprintln!("\n===================================================="); eprintln!();
eprintln!("[FATAL ERROR] Program terminated unexpectedly:"); eprintln!("[ostp] Fatal error: {}", e);
eprintln!(" {}", e); eprintln!();
eprintln!("====================================================");
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
{ {
@ -270,13 +269,13 @@ fn get_or_ask_public_ip(config_path: &std::path::Path) -> String {
} }
if let Some(detected) = detect_local_public_ip() { if let Some(detected) = detect_local_public_ip() {
println!("[OSTP Core] Auto-detected public network IP: {}", detected); println!("[ostp] Detected public IP: {}", detected);
let _ = std::fs::write(&cache_path, &detected); let _ = std::fs::write(&cache_path, &detected);
return detected; return detected;
} }
print!("\n[OSTP Core] Could not automatically detect your Server's Public IP.\n"); print!("\n[ostp] Could not detect the server public IP automatically.\n");
print!(">>> Please enter your Public IP or Domain Name for user links: "); print!(" Enter your public IP or domain: ");
use std::io::Write; use std::io::Write;
let _ = std::io::stdout().flush(); let _ = std::io::stdout().flush();
@ -303,7 +302,7 @@ async fn run_app() -> Result<()> {
} }
if let Some(url) = args.url { if let Some(url) = args.url {
println!("[OSTP Core] Booting direct client connection via share link..."); println!("[ostp] Connecting via share link...");
let client_cfg = parse_ostp_link(&url) let client_cfg = parse_ostp_link(&url)
.map_err(|e| anyhow!("Share Link Error: {e}"))?; .map_err(|e| anyhow!("Share Link Error: {e}"))?;
return run_client_directly(client_cfg).await; return run_client_directly(client_cfg).await;
@ -390,7 +389,7 @@ async fn run_app() -> Result<()> {
}}"#, key) }}"#, key)
}; };
fs::write(&args.config, &content)?; fs::write(&args.config, &content)?;
println!("Successfully initialized configuration at {:?}", args.config); println!("[ostp] Configuration written to {:?}", args.config);
if is_server { if is_server {
let mut stripped = json_comments::StripComments::new(content.as_bytes()); let mut stripped = json_comments::StripComments::new(content.as_bytes());
@ -398,7 +397,7 @@ async fn run_app() -> Result<()> {
if let AppMode::Server(s) = config.mode { if let AppMode::Server(s) = config.mode {
let key = &s.access_keys[0]; let key = &s.access_keys[0];
let host = get_or_ask_public_ip(&args.config); let host = get_or_ask_public_ip(&args.config);
println!("\n>>> Handy Client Share Link for your users:"); println!("\n Share link for client distribution:");
println!(" ostp://{}@{}:50000", key, host); println!(" ostp://{}@{}:50000", key, host);
} }
} }
@ -439,7 +438,7 @@ async fn run_app() -> Result<()> {
parts[0].to_string() parts[0].to_string()
}; };
println!("\n>>> Ready-to-use OSTP client share links from {:?}:", args.config); println!("\n Client share links from {:?}:", args.config);
for (idx, key) in server_cfg.access_keys.iter().enumerate() { for (idx, key) in server_cfg.access_keys.iter().enumerate() {
println!(" [{}] ostp://{}@{}:{}", idx + 1, key, host, port); println!(" [{}] ostp://{}@{}:{}", idx + 1, key, host, port);
} }
@ -453,9 +452,9 @@ async fn run_app() -> Result<()> {
match config.mode { match config.mode {
AppMode::Server(server_cfg) => { AppMode::Server(server_cfg) => {
println!("[OSTP Core] Starting in SERVER mode on {}", server_cfg.listen); println!("[ostp] Starting server on {}", server_cfg.listen);
if let Some(turn) = server_cfg.turn_server { if let Some(turn) = server_cfg.turn_server {
println!("[OSTP Core] TURN integration enabled: {}", turn); println!("[ostp] TURN relay enabled: {}", turn);
} }
// Temporarily pass control to the isolated server implementation // Temporarily pass control to the isolated server implementation
let debug = server_cfg.debug.unwrap_or(false); let debug = server_cfg.debug.unwrap_or(false);
@ -487,17 +486,9 @@ async fn run_app() -> Result<()> {
} }
async fn run_client_directly(client_cfg: ClientConfig) -> Result<()> { async fn run_client_directly(client_cfg: ClientConfig) -> Result<()> {
println!("[OSTP Core] Starting in CLIENT mode connecting to {}", client_cfg.server);
if let Some(ref tun) = client_cfg.tun {
if tun.enable {
println!("[OSTP Core] TUN mode enabled.");
if let Some(ref path) = tun.wintun_path {
println!("[OSTP Core] Using custom wintun path: {}", path);
}
}
}
println!("[OSTP Core] Client logic loaded.");
let is_tun_enabled = client_cfg.tun.as_ref().map(|t| t.enable).unwrap_or(false); let is_tun_enabled = client_cfg.tun.as_ref().map(|t| t.enable).unwrap_or(false);
let mode_str = if is_tun_enabled { "tun" } else { "proxy" };
println!("[ostp] Starting client (mode={}, server={})", mode_str, client_cfg.server);
let turn_cfg = client_cfg.turn.as_ref(); let turn_cfg = client_cfg.turn.as_ref();
let client_conf = ostp_client::config::ClientConfig { let client_conf = ostp_client::config::ClientConfig {
mode: if is_tun_enabled { "tun".to_string() } else { "proxy".to_string() }, mode: if is_tun_enabled { "tun".to_string() } else { "proxy".to_string() },