From fbc37e9d391d365573aacac6483566b9ed05f60e Mon Sep 17 00:00:00 2001 From: ospab Date: Wed, 8 Jul 2026 22:45:03 +0300 Subject: [PATCH 1/3] fix(gui): fix UAC SmartScreen and scrolling UI layout --- ostp-gui/src-tauri/src/lib.rs | 32 ++++++++++++++++++++++++++++---- ostp-gui/src/index.html | 7 ++++--- ostp-gui/src/styles.css | 20 +++++++++----------- 3 files changed, 41 insertions(+), 18 deletions(-) diff --git a/ostp-gui/src-tauri/src/lib.rs b/ostp-gui/src-tauri/src/lib.rs index 1fa92fa..0298fe3 100644 --- a/ostp-gui/src-tauri/src/lib.rs +++ b/ostp-gui/src-tauri/src/lib.rs @@ -766,10 +766,34 @@ fn launch_as_admin(exe: &std::path::PathBuf, token: &str, port: u16) -> anyhow:: // Use the GUI executable's directory as the working directory so dependencies are found let cwd_path = std::env::current_exe().unwrap_or_else(|_| std::path::PathBuf::from(".")); let dir_wstr: Vec = cwd_path.parent().unwrap_or(std::path::Path::new(".")).as_os_str().encode_wide().chain(Some(0)).collect(); - - let ret = unsafe { ShellExecuteW(null_mut(), verb_wstr.as_ptr(), exe_wstr.as_ptr(), params_wstr.as_ptr(), dir_wstr.as_ptr(), 0) }; - - if ret <= 32 { anyhow::bail!("UAC denied or helper missing."); } + // Remove Mark of the Web (Zone.Identifier) so SmartScreen doesn't block UAC + let zone_id = format!("{}:Zone.Identifier", exe.display()); + let _ = std::fs::remove_file(zone_id); + + // Use SW_SHOWNORMAL (1) instead of SW_HIDE (0) because runas with SW_HIDE is automatically blocked by UAC + let ret = unsafe { ShellExecuteW(null_mut(), verb_wstr.as_ptr(), exe_wstr.as_ptr(), params_wstr.as_ptr(), dir_wstr.as_ptr(), 1) }; + + // ShellExecuteW's return is a pseudo-HINSTANCE: > 32 means the call itself + // "succeeded" — but that range INCLUDES ERROR_CANCELLED (1223), which is + // exactly what Windows returns when the user clicks "No" on the UAC prompt. + // The old `ret <= 32` check alone treated a user-denied prompt as success, + // silently starting nothing and reporting a single opaque "denied or + // missing" message that could not distinguish "no prompt ever shown" + // (missing exe, ret<=32) from "prompt shown and declined" (ret==1223) from + // any other Win32 failure — exactly the ambiguity blocking diagnosis here. + if ret == 1223 { + anyhow::bail!("UAC elevation was denied. TUN mode requires administrator privileges."); + } + if ret <= 32 { + let win_err = unsafe { GetLastError() }; + anyhow::bail!( + "Failed to request UAC elevation for the TUN helper (ShellExecuteW ret={}, \ + GetLastError={}, path={}). If this keeps happening with no prompt ever appearing, \ + an unsigned binary can be silently blocked by SmartScreen/antivirus during \ + elevation — try running ostp-gui.exe as Administrator manually.", + ret, win_err, exe.display() + ); + } Ok(()) } diff --git a/ostp-gui/src/index.html b/ostp-gui/src/index.html index 411112b..9c84311 100644 --- a/ostp-gui/src/index.html +++ b/ostp-gui/src/index.html @@ -280,10 +280,11 @@ - + +
OSTP GUI
- - + + -
-
+
+
RTT --
-
-
+
+
0 B/s
-
-
+
+
0 B/s
@@ -280,6 +280,28 @@
+
+
+ Show RTT + Display live ping on dashboard +
+ +
+ +
+
+ Show Speed + Display traffic speed on dashboard +
+ +
+
OSTP GUI
diff --git a/ostp-gui/src/main.js b/ostp-gui/src/main.js index 3d83cfe..b6cb103 100644 --- a/ostp-gui/src/main.js +++ b/ostp-gui/src/main.js @@ -154,6 +154,8 @@ const inExProcs = $('in-ex-procs'); const inAutoconnect = $('in-autoconnect'); const inLaunchStartup = $('in-launch-startup'); const inDebug = $('in-debug'); +const inShowRtt = $('in-show-rtt'); +const inShowSpeed = $('in-show-speed'); const groupKillSwitch = $('group-kill-switch'); const groupMuxSessions = $('group-mux-sessions'); @@ -664,6 +666,8 @@ function loadSettingsIntoForm() { inAutoconnect.checked = !!s.autoconnect; inLaunchStartup.checked = !!s.launchStartup; inDebug.checked = !!s.debug; + inShowRtt.checked = s.showRtt !== false; + inShowSpeed.checked = s.showSpeed !== false; updateClientVisibility(); } @@ -682,6 +686,8 @@ function collectAndSaveSettings() { autoconnect: inAutoconnect.checked, launchStartup: inLaunchStartup.checked, debug: inDebug.checked, + showRtt: inShowRtt.checked, + showSpeed: inShowSpeed.checked, }; saveClientSettings(s); updateClientVisibility(); @@ -703,6 +709,23 @@ function collectAndSaveSettings() { function updateClientVisibility() { groupKillSwitch.style.display = inTun.checked ? 'flex' : 'none'; groupMuxSessions.style.display = inMux.checked ? 'flex' : 'none'; + + const showRtt = inShowRtt.checked; + const showSpeed = inShowSpeed.checked; + const rttBox = $('stat-rtt-box'); + const downBox = $('stat-down-box'); + const upBox = $('stat-up-box'); + const sep1 = $('stat-sep-1'); + const sep2 = $('stat-sep-2'); + const container = $('live-stats-container'); + + if (rttBox) rttBox.style.display = showRtt ? 'flex' : 'none'; + if (downBox) downBox.style.display = showSpeed ? 'flex' : 'none'; + if (upBox) upBox.style.display = showSpeed ? 'flex' : 'none'; + + if (sep1) sep1.style.display = (showRtt && showSpeed) ? 'block' : 'none'; + if (sep2) sep2.style.display = showSpeed ? 'block' : 'none'; + if (container) container.style.display = (showRtt || showSpeed) ? 'flex' : 'none'; } // ── INIT ────────────────────────────────────────────────────────────── @@ -858,7 +881,7 @@ window.addEventListener('DOMContentLoaded', async () => { wintunModal.addEventListener('click', e => { if (e.target === wintunModal) wintunModal.classList.add('hidden'); }); // Client settings — wire all inputs - [inTun, inKillSwitch, inMux, inAutoconnect, inLaunchStartup, inDebug] + [inTun, inKillSwitch, inMux, inAutoconnect, inLaunchStartup, inDebug, inShowRtt, inShowSpeed] .forEach(el => el.addEventListener('change', collectAndSaveSettings)); [inMuxSessions, inMtu, inDns, inSocks, inExDomains, inExIps, inExProcs] .forEach(el => { From 244d3ad374d58e52df31df8a8cc98f2446d201ae Mon Sep 17 00:00:00 2001 From: ospab Date: Thu, 9 Jul 2026 01:24:43 +0300 Subject: [PATCH 3/3] ci: update GHA run-name format for releases --- .github/workflows/release.yml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a0e8b52..30c03c3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,6 +1,19 @@ name: CI/CD -run-name: "CI/CD: release version ${{ github.ref_name }}" +# `run-name` is evaluated at workflow-start, BEFORE any job runs — it cannot +# see resolve-channel's computed tag_name (e.g. "0.4.3-nightly"), only the +# `github.*` context. The old "release version ${{ github.ref_name }}" showed +# the bare branch name ("nightly"/"pre-release") for every run, which reads +# exactly like a literal release tag and caused real confusion — the actual +# release tag has been correct (versioned) all along; only this label lied +# about it. Spell out "channel" so nobody mistakes one for the other again. +# NOTE: this value MUST be quoted. The GHA string literal below contains +# "Release build: {0}" — an unquoted YAML plain scalar treats ": " (colon +# then space) as starting a nested mapping, which is exactly what broke every +# single push since this line was introduced: GitHub rejected the whole +# workflow file at parse time (before any job runs), silently burning an +# Actions-minutes-billed run per push for nothing. +run-name: "${{ startsWith(github.ref, 'refs/tags/') && (contains(github.ref_name, 'beta') && format('CI/CD: beta version {0}', github.ref_name) || contains(github.ref_name, 'nightly') && format('CI/CD: nightly version {0}', github.ref_name) || format('CI/CD: release version {0}', github.ref_name)) || format('CI/CD: {0} channel build', github.ref_name) }}" on: push: