fix(gui): honest TUN error on Linux, and a window that can be resized

Reported from the Linux GUI: it asked for "helper.exe" on Linux, and the
window was tiny.

The helper name had ".exe" hardcoded in every lookup path, so on Linux the
search could only ever fail. Fixing the name alone would have been misleading
though, because TUN mode does not work on Linux for a deeper reason:
launch_as_admin is `bail!("Windows only.")` outside Windows, and the release
workflow only builds ostp-tun-helper in the Windows GUI job. So the feature is
Windows-only, and the message now says exactly that and points at proxy mode,
instead of surfacing as a missing file named after a Windows executable —
which reads like a packaging mistake rather than an unimplemented feature. The
name is still resolved per-platform for when Linux elevation does land.

The window was 360x680 and `resizable: false`. Windows scales that by DPI, but
WebKitGTK on a HiDPI Linux display renders it close to raw pixels, giving a
postage-stamp window the user then could not resize. It is now resizable with
a sensible minimum, and .app-root caps and centres the column so a wider
window keeps the intended narrow layout instead of stretching the controls.
This commit is contained in:
ospab 2026-08-04 00:56:28 +03:00
parent e483af541f
commit 8a1426ecf5
3 changed files with 52 additions and 15 deletions

View File

@ -625,13 +625,30 @@ async fn start_tun_via_helper(
raw: &ClientConfigRaw,
app: tauri::AppHandle,
) -> Result<bool, String> {
// TUN mode goes through a privileged helper, and the only elevation path
// implemented is the Windows UAC one (see launch_as_admin). The helper is
// also not built for other platforms by the release workflow. Say that
// plainly and up front: previously this fell through to the helper lookup
// and surfaced as a missing-file error naming a Windows executable, which
// on Linux reads as a packaging mistake rather than an unimplemented
// feature.
if !cfg!(windows) {
return Err(
"TUN mode is currently Windows-only: it needs a privileged helper, and elevation \
for it is only implemented on Windows. Use proxy mode (SOCKS5/HTTP) on this \
platform."
.to_string(),
);
}
let port = {
let listener = std::net::TcpListener::bind("127.0.0.1:0").map_err(|e| format!("Bind error: {}", e))?;
listener.local_addr().unwrap().port()
};
let auth_token = rand::random::<u64>().to_string();
let helper_exe = find_helper_exe().ok_or_else(|| "ostp-tun-helper.exe not found.".to_string())?;
let helper_exe = find_helper_exe()
.ok_or_else(|| format!("{HELPER_EXE_NAME} not found next to the app or in target/."))?;
launch_as_admin(&helper_exe, &auth_token, port).map_err(|e| format!("Failed to launch helper: {}", e))?;
tokio::time::sleep(std::time::Duration::from_millis(1500)).await;
@ -705,11 +722,22 @@ struct HelperPipeState {
error_msg: Option<String>,
}
/// Executable name of the TUN helper for the current platform.
///
/// The ".exe" suffix was hardcoded, so on Linux every lookup below searched for
/// a file that cannot exist and the GUI reported the helper as missing on a
/// platform where it ships without an extension.
const HELPER_EXE_NAME: &str = if cfg!(windows) {
"ostp-tun-helper.exe"
} else {
"ostp-tun-helper"
};
fn find_helper_exe() -> Option<PathBuf> {
if let Ok(exe) = std::env::current_exe() {
if let Some(dir) = exe.parent() {
// 1. Release/Production adjacent
let candidate = dir.join("ostp-tun-helper.exe");
let candidate = dir.join(HELPER_EXE_NAME);
if candidate.exists() { return Some(candidate); }
// 2. Tauri target directory fallback
@ -717,9 +745,9 @@ fn find_helper_exe() -> Option<PathBuf> {
let mut parent = dir;
while let Some(p) = parent.parent() {
if p.file_name().map(|n| n == "target").unwrap_or(false) {
let deb = p.join("debug").join("ostp-tun-helper.exe");
let deb = p.join("debug").join(HELPER_EXE_NAME);
if deb.exists() { return Some(deb); }
let rel = p.join("release").join("ostp-tun-helper.exe");
let rel = p.join("release").join(HELPER_EXE_NAME);
if rel.exists() { return Some(rel); }
}
parent = p;
@ -729,13 +757,13 @@ fn find_helper_exe() -> Option<PathBuf> {
// 3. Current working directory target fallback
let cwd = std::env::current_dir().unwrap_or_default();
let candidates = [
cwd.join("ostp-tun-helper.exe"),
cwd.join("target").join("debug").join("ostp-tun-helper.exe"),
cwd.join("target").join("release").join("ostp-tun-helper.exe"),
cwd.join("..").join("target").join("debug").join("ostp-tun-helper.exe"),
cwd.join("..").join("target").join("release").join("ostp-tun-helper.exe"),
cwd.join("..").join("..").join("target").join("debug").join("ostp-tun-helper.exe"),
cwd.join("..").join("..").join("target").join("release").join("ostp-tun-helper.exe"),
cwd.join(HELPER_EXE_NAME),
cwd.join("target").join("debug").join(HELPER_EXE_NAME),
cwd.join("target").join("release").join(HELPER_EXE_NAME),
cwd.join("..").join("target").join("debug").join(HELPER_EXE_NAME),
cwd.join("..").join("target").join("release").join(HELPER_EXE_NAME),
cwd.join("..").join("..").join("target").join("debug").join(HELPER_EXE_NAME),
cwd.join("..").join("..").join("target").join("release").join(HELPER_EXE_NAME),
];
for path in &candidates {
if path.exists() { return Some(path.clone()); }

View File

@ -11,9 +11,11 @@
"windows": [
{
"title": "OSTP",
"width": 360,
"height": 680,
"resizable": false
"width": 400,
"height": 720,
"minWidth": 360,
"minHeight": 560,
"resizable": true
}
],
"security": {

View File

@ -99,6 +99,13 @@ a { text-decoration: none; }
.app-root {
position: relative;
width: 100%;
/* The window is resizable so users on desktops where the toolkit does not
apply our DPI scaling (WebKitGTK on HiDPI Linux renders the configured
size as raw pixels, giving a postage-stamp window) can size it themselves.
Capping and centring the column keeps the intended narrow layout instead of
stretching controls across a wide window. */
max-width: 460px;
margin: 0 auto;
height: 100%;
display: flex;
flex-direction: column;