From f7684f9f4d0a5e8e8e049237f27fdf1b85801a52 Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Thu, 21 May 2026 16:19:02 +0530 Subject: [PATCH 01/17] fix(tls): use native TLS for core HTTP clients MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch reqwest builders in `api::rest` and `openhuman::app_state` from `use_rustls_tls()` to `use_native_tls()` so the platform trust store is honored. Why: rustls + webpki-roots only ships Mozilla root CAs. In TLS-inspecting environments (corporate antivirus, MITM proxies) the OS chain validates against a locally installed CA that webpki-roots cannot see, so every HTTPS call to `api.tinyhumans.ai` fails with `UnknownIssuer`. Native TLS uses schannel on Windows, SecureTransport on macOS, and OpenSSL on Linux — each defers to the OS trust store, which already holds the corporate CA when present. --- src/api/rest.rs | 7 +++++-- src/openhuman/app_state/ops.rs | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/api/rest.rs b/src/api/rest.rs index 3c2e481bbb..99134e83bd 100644 --- a/src/api/rest.rs +++ b/src/api/rest.rs @@ -78,10 +78,13 @@ fn build_backend_reqwest_client() -> Result { ); } - // Force rustls for consistent cross-platform TLS behavior. + // Use the platform TLS stack (schannel/SecureTransport/OpenSSL) so the + // OS trust store is honored — needed when running behind corporate + // TLS-inspecting proxies that present a re-signed cert from a CA only + // trusted by the OS, not by rustls's bundled webpki-roots. Client::builder() .default_headers(default_headers) - .use_rustls_tls() + .use_native_tls() .http1_only() .timeout(Duration::from_secs(120)) .connect_timeout(Duration::from_secs(15)) diff --git a/src/openhuman/app_state/ops.rs b/src/openhuman/app_state/ops.rs index 5e65174231..0af1425fce 100644 --- a/src/openhuman/app_state/ops.rs +++ b/src/openhuman/app_state/ops.rs @@ -263,8 +263,9 @@ fn save_stored_app_state(config: &Config, state: &StoredAppState) -> Result<(), } fn build_client() -> Result { + // Native TLS so the OS trust store is honored — see [`crate::api::rest`]. Client::builder() - .use_rustls_tls() + .use_native_tls() .http1_only() .timeout(Duration::from_secs(30)) .connect_timeout(Duration::from_secs(10)) From 36c74174d00c0e05565f235951aeb72413043bd8 Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Thu, 21 May 2026 16:23:09 +0530 Subject: [PATCH 02/17] fix(tls): switch WebSocket TLS to native-tls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Swap `tokio-tungstenite` feature from `rustls-tls-webpki-roots` to `native-tls` so the platform trust store is honored on connect. Why: matches the HTTP-client switch in the parent commit. With webpki-roots, the WebSocket handshake to `wss://api.tinyhumans.ai` fails with `UnknownIssuer` in TLS-inspecting environments because the intercepting CA isn't in the Mozilla bundle. `connect_async()` picks its TLS connector based on the enabled feature, so the call sites in `socket::ws_loop` don't need changes — only the Cargo feature does. Cargo.lock will be regenerated on the next build. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 58ef0e58db..ddb3f4b9d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,7 +76,7 @@ async-trait = "0.1" chacha20poly1305 = "0.10" hex = "0.4" tokio-util = { version = "0.7", features = ["rt", "io"] } -tokio-tungstenite = { version = "0.24", features = ["rustls-tls-webpki-roots"] } +tokio-tungstenite = { version = "0.24", features = ["native-tls"] } futures = "0.3" rusqlite = { version = "0.37", features = ["bundled"] } chrono = { version = "0.4", features = ["serde"] } From 3b7eb02961772d8fe74996c85ebb82b567013086 Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Thu, 21 May 2026 16:27:53 +0530 Subject: [PATCH 03/17] fix(run-dev-win): prepend workspace node_modules/.bin to PATH MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the dev script reaches `pnpm tauri dev`, pnpm spawns cmd.exe to run the literal script body `tauri "dev"`. Pnpm normally prepends `./node_modules/.bin` for that child shell, but under the long bash→cmd→bash→pnpm chain produced by `pnpm dev:app:win`, the relative entry sometimes resolves against the wrong cwd and `tauri.CMD` is not found ("'tauri' is not recognized as an internal or external command"). Prepend the absolute `$APP_DIR/node_modules/.bin` to PATH before any `pnpm tauri:ensure` / `pnpm tauri dev` invocation so the shim is always resolvable regardless of how pnpm propagates PATH downstream. --- scripts/run-dev-win.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/run-dev-win.sh b/scripts/run-dev-win.sh index 8492a24da7..18cabc7ff6 100644 --- a/scripts/run-dev-win.sh +++ b/scripts/run-dev-win.sh @@ -507,6 +507,13 @@ PATH_PREFIX="/c/Program Files/CMake/bin:$(dirname "$NINJA_EXE")" if [[ -n "${CEF_RUNTIME_PATH:-}" ]]; then PATH_PREFIX="$PATH_PREFIX:$CEF_RUNTIME_PATH" fi +# Ensure the workspace node_modules/.bin is on PATH so pnpm's child +# spawns (e.g. `pnpm tauri dev` → `tauri.CMD`) can resolve the shims. +# Pnpm normally prepends `./node_modules/.bin` for script execution, but +# when the script body is `tauri "dev"` and the child shell is cmd.exe +# under the long bash→cmd→bash chain, the relative entry sometimes +# resolves against the wrong cwd and tauri.CMD is not found. +PATH_PREFIX="$APP_DIR/node_modules/.bin:$PATH_PREFIX" export PATH="$PATH_PREFIX:$PATH" "$PNPM_EXE" tauri:ensure From 8413582e7039e230cdc6d73ce4e245afb644b2a8 Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Thu, 21 May 2026 16:30:50 +0530 Subject: [PATCH 04/17] fix(run-dev-win): invoke cargo-tauri.exe directly, skip pnpm wrapper The final `pnpm tauri dev` step in the dev script is fragile on Windows. It depends on which pnpm shim `find_pnpm` resolved: - `~/AppData/Local/pnpm/.tools/.../pnpm` (the self-managing shim) auto- prepends `node_modules/.bin` to PATH when forking the script's cmd.exe child, so the literal script body `tauri "dev"` resolves `tauri.CMD` from the workspace bin dir. - The WinGet-installed `pnpm.exe` does not perform the same auto- prepend, so the same script body fails with "'tauri' is not recognized as an internal or external command". Which shim wins is racey (depends on bash's PATH ordering, /etc/profile behavior, and whether pnpm self-update fixed any placeholder files). Side-step the wrapper by calling the vendored `cargo-tauri.exe` binary that `ensure-tauri-cli.sh` already installs at `/.cache/cargo-install/bin/`. Same effect, no pnpm-shim dependency. The DEV_PORT/devUrl override path is preserved verbatim. --- scripts/run-dev-win.sh | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/scripts/run-dev-win.sh b/scripts/run-dev-win.sh index 18cabc7ff6..202e3e10b8 100644 --- a/scripts/run-dev-win.sh +++ b/scripts/run-dev-win.sh @@ -583,9 +583,28 @@ else DEV_PORT=1420 fi +# Invoke cargo-tauri directly rather than going through `pnpm tauri dev`. +# +# The pnpm chain (pnpm.exe → cmd.exe → tauri.CMD) is fragile on Windows: +# whether `tauri.CMD` is resolvable in the spawned cmd subprocess depends +# on which pnpm shim was picked up by `find_pnpm`. The self-managing +# `~/AppData/Local/pnpm/.tools/.../pnpm` variant auto-prepends +# `node_modules/.bin` for script children; the WinGet-installed +# `pnpm.exe` does not, so the script body `tauri "dev"` then fails with +# "'tauri' is not recognized" inside cmd. +# +# `ensure-tauri-cli.sh` already installed the vendored CEF-aware +# cargo-tauri at `$REPO_ROOT/.cache/cargo-install/bin/cargo-tauri.exe`, +# so we can invoke that binary directly and skip the wrapper layer. +CARGO_TAURI_EXE="$REPO_ROOT/.cache/cargo-install/bin/cargo-tauri.exe" +if [[ ! -x "$CARGO_TAURI_EXE" ]]; then + echo "[run-dev-win] cargo-tauri.exe not found at $CARGO_TAURI_EXE" >&2 + echo "[run-dev-win] tauri:ensure should have installed it. Aborting." >&2 + exit 1 +fi if (( DEV_PORT != 1420 )); then echo "[run-dev-win] OPENHUMAN_DEV_PORT=$DEV_PORT — overriding tauri devUrl" - "$PNPM_EXE" tauri dev -c "{\"build\":{\"devUrl\":\"http://localhost:$DEV_PORT\"}}" + "$CARGO_TAURI_EXE" dev -c "{\"build\":{\"devUrl\":\"http://localhost:$DEV_PORT\"}}" else - "$PNPM_EXE" tauri dev + "$CARGO_TAURI_EXE" dev fi From 118ed8240daea4246026d3d7b2dc54f8c8896c31 Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Thu, 21 May 2026 16:34:23 +0530 Subject: [PATCH 05/17] fix(run-dev-win): pin pnpm dir on PATH for cargo-tauri's beforeDevCommand MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After the previous commit switched the final dev launcher from `pnpm tauri dev` to `cargo-tauri.exe dev`, cargo-tauri spawns the configured `beforeDevCommand` (`pnpm run dev`) in a fresh cmd.exe child that inherits PATH from cargo-tauri but does NOT inherit pnpm's own runtime PATH injection. With the WinGet-installed pnpm in particular (no `AppData/Roaming/npm` shim), the spawned cmd then fails with "'pnpm' is not recognized as an internal or external command". Prepend the directory holding the resolved `$PNPM_EXE` to PATH_PREFIX so the chain `bash → cargo-tauri.exe → cmd → pnpm → vite` finds pnpm regardless of which pnpm shim find_pnpm picked. --- scripts/run-dev-win.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/scripts/run-dev-win.sh b/scripts/run-dev-win.sh index 202e3e10b8..7a1a5adf9e 100644 --- a/scripts/run-dev-win.sh +++ b/scripts/run-dev-win.sh @@ -514,6 +514,17 @@ fi # under the long bash→cmd→bash chain, the relative entry sometimes # resolves against the wrong cwd and tauri.CMD is not found. PATH_PREFIX="$APP_DIR/node_modules/.bin:$PATH_PREFIX" + +# Ensure pnpm itself stays on PATH for cargo-tauri's beforeDevCommand +# (`pnpm run dev` → vite). When run-dev-win.sh restores the Windows PATH +# via cmd.exe %PATH%, some setups (WinGet-installed pnpm with no +# AppData/Roaming/npm entry) don't surface a pnpm dir consistently +# downstream. Prepend the resolved $PNPM_EXE dir to guarantee it. +if [[ -n "${PNPM_EXE:-}" ]]; then + PNPM_EXE_DIR="$(dirname "$PNPM_EXE")" + PATH_PREFIX="$PNPM_EXE_DIR:$PATH_PREFIX" +fi + export PATH="$PATH_PREFIX:$PATH" "$PNPM_EXE" tauri:ensure From 9055ee23320cb954570f9a5d20abf5bde2a46017 Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Thu, 21 May 2026 16:36:26 +0530 Subject: [PATCH 06/17] fix(run-dev-win): override tauri beforeDevCommand with absolute pnpm path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tauri.conf.json's `beforeDevCommand` is `"pnpm run dev"` (bare name). cargo-tauri spawns it via `cmd.exe /S /C pnpm run dev`, and that cmd child does not reliably see the same PATH that the parent bash had after vcvars rewrites and MSYS conversion — pnpm fails with "'pnpm' is not recognized as an internal or external command". Pinning the pnpm path on PATH inside this script (previous commit) isn't enough: by the time the path makes it through the bash → cargo-tauri.exe → cmd.exe chain, the entry may be reordered, stripped, or path-converted incorrectly. Defense-in-depth: override the tauri config inline so the resolved absolute `$PNPM_EXE` (converted to a Windows path) is invoked directly, e.g. `"C:\Users\…\pnpm.exe" run dev`. The merge happens via the same `-c` JSON path that DEV_PORT already uses for devUrl. --- scripts/run-dev-win.sh | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/scripts/run-dev-win.sh b/scripts/run-dev-win.sh index 7a1a5adf9e..b1ce7a42bb 100644 --- a/scripts/run-dev-win.sh +++ b/scripts/run-dev-win.sh @@ -613,9 +613,23 @@ if [[ ! -x "$CARGO_TAURI_EXE" ]]; then echo "[run-dev-win] tauri:ensure should have installed it. Aborting." >&2 exit 1 fi + +# Build a tauri.conf.json `-c` JSON merge that: +# - pins `beforeDevCommand` to the absolute pnpm path so cargo-tauri's +# cmd.exe child can find pnpm regardless of any PATH stripping +# between bash → cargo-tauri → cmd. The default in tauri.conf.json +# is `"pnpm run dev"` (bare name) which depends on PATH. +# - overrides `devUrl` when OPENHUMAN_DEV_PORT is non-default. +PNPM_EXE_WIN="$(cygpath -w "$PNPM_EXE" 2>/dev/null || printf '%s' "$PNPM_EXE")" +# JSON-escape the Windows path's backslashes for embedding in the -c arg. +PNPM_EXE_JSON="${PNPM_EXE_WIN//\\/\\\\}" +BEFORE_DEV_CMD="\\\"$PNPM_EXE_JSON\\\" run dev" +CONFIG_OVERRIDE="{\"build\":{\"beforeDevCommand\":\"$BEFORE_DEV_CMD\"" if (( DEV_PORT != 1420 )); then echo "[run-dev-win] OPENHUMAN_DEV_PORT=$DEV_PORT — overriding tauri devUrl" - "$CARGO_TAURI_EXE" dev -c "{\"build\":{\"devUrl\":\"http://localhost:$DEV_PORT\"}}" -else - "$CARGO_TAURI_EXE" dev + CONFIG_OVERRIDE+=",\"devUrl\":\"http://localhost:$DEV_PORT\"" fi +CONFIG_OVERRIDE+="}}" + +echo "[run-dev-win] tauri config override: $CONFIG_OVERRIDE" +"$CARGO_TAURI_EXE" dev -c "$CONFIG_OVERRIDE" From 005a19092922ec2620aa00625844a9ff4f863335 Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Thu, 21 May 2026 16:44:00 +0530 Subject: [PATCH 07/17] fix(run-dev-win): drop literal quotes from beforeDevCommand override MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Embedding quotes around the absolute pnpm path in the tauri config override (`"" run dev`) survives the JSON decode but loses the quotes when cargo-tauri hands the string to `cmd.exe /S /C`: Rust's argv-to-cmd escaping rewrites `"` to `\"`, and cmd then treats the whole `\"…\"` token as the program name and errors out with "'\"C:\…\pnpm\"' is not recognized". The pnpm install paths we resolve via `find_pnpm` are space-free (`@pnpm+exe/10.10.0` is dot- and plus-laden, but no spaces), so passing a bare path avoids the quote-mangling entirely. Backslashes still need JSON-escaping for the `-c` argument, which is preserved. --- scripts/run-dev-win.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/run-dev-win.sh b/scripts/run-dev-win.sh index b1ce7a42bb..0036756490 100644 --- a/scripts/run-dev-win.sh +++ b/scripts/run-dev-win.sh @@ -622,8 +622,13 @@ fi # - overrides `devUrl` when OPENHUMAN_DEV_PORT is non-default. PNPM_EXE_WIN="$(cygpath -w "$PNPM_EXE" 2>/dev/null || printf '%s' "$PNPM_EXE")" # JSON-escape the Windows path's backslashes for embedding in the -c arg. +# No embedded double-quotes around the path: cargo-tauri spawns +# `cmd.exe /S /C ` and Rust's argv escaping rewrites quoted +# tokens to `\"…\"`, which cmd then treats as a literal filename. None +# of the pnpm install paths we resolve have spaces (`@pnpm+exe/10.10.0` +# is dot- and plus-laden but space-free), so a bare path is safe. PNPM_EXE_JSON="${PNPM_EXE_WIN//\\/\\\\}" -BEFORE_DEV_CMD="\\\"$PNPM_EXE_JSON\\\" run dev" +BEFORE_DEV_CMD="$PNPM_EXE_JSON run dev" CONFIG_OVERRIDE="{\"build\":{\"beforeDevCommand\":\"$BEFORE_DEV_CMD\"" if (( DEV_PORT != 1420 )); then echo "[run-dev-win] OPENHUMAN_DEV_PORT=$DEV_PORT — overriding tauri devUrl" From 57c54c8cd092dd22bc873626330356fed78a219a Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Thu, 21 May 2026 16:45:27 +0530 Subject: [PATCH 08/17] fix(run-dev-win): invoke vite directly in beforeDevCommand, skip pnpm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous fix routed the override through ` run dev`, which then runs `vite` via pnpm. cargo-tauri spawns beforeDevCommand from the tauri project dir (`app/src-tauri/`), so pnpm's automatic `./node_modules/.bin` prepend resolves against `app/src-tauri/` and fails to find `vite` ("'vite' is not recognized"). `pnpm run dev` ultimately just executes `vite` from `app/node_modules/.bin/vite.CMD`. Point beforeDevCommand at that path directly — same behavior, no cwd dependency, no pnpm path quirks. If `vite.CMD` is missing we bail with a clear "did pnpm install run?" message instead of letting cargo-tauri report an opaque cmd failure. --- scripts/run-dev-win.sh | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/scripts/run-dev-win.sh b/scripts/run-dev-win.sh index 0036756490..72f21b0351 100644 --- a/scripts/run-dev-win.sh +++ b/scripts/run-dev-win.sh @@ -620,15 +620,26 @@ fi # between bash → cargo-tauri → cmd. The default in tauri.conf.json # is `"pnpm run dev"` (bare name) which depends on PATH. # - overrides `devUrl` when OPENHUMAN_DEV_PORT is non-default. -PNPM_EXE_WIN="$(cygpath -w "$PNPM_EXE" 2>/dev/null || printf '%s' "$PNPM_EXE")" -# JSON-escape the Windows path's backslashes for embedding in the -c arg. -# No embedded double-quotes around the path: cargo-tauri spawns -# `cmd.exe /S /C ` and Rust's argv escaping rewrites quoted -# tokens to `\"…\"`, which cmd then treats as a literal filename. None -# of the pnpm install paths we resolve have spaces (`@pnpm+exe/10.10.0` -# is dot- and plus-laden but space-free), so a bare path is safe. -PNPM_EXE_JSON="${PNPM_EXE_WIN//\\/\\\\}" -BEFORE_DEV_CMD="$PNPM_EXE_JSON run dev" +# Point beforeDevCommand at vite directly, not through `pnpm run dev`. +# +# cargo-tauri runs the beforeDevCommand from the tauri project dir +# (`app/src-tauri/`), so any `pnpm run dev` invocation inherits that +# cwd. Pnpm then prepends `./node_modules/.bin` to PATH for the script +# child — but `./` resolves against the inherited cwd, i.e. +# `app/src-tauri/node_modules/.bin`, which doesn't exist. The result is +# "'vite' is not recognized" inside cmd. +# +# Skip pnpm altogether and reference the vite shim via its absolute +# Windows path. Same effect as `pnpm run dev`, no path-resolution +# dependency on whichever cwd cargo-tauri chose. +VITE_BIN_UNIX="$APP_DIR/node_modules/.bin/vite.CMD" +if [[ ! -f "$VITE_BIN_UNIX" ]]; then + echo "[run-dev-win] vite shim not found at $VITE_BIN_UNIX" >&2 + echo "[run-dev-win] Did `pnpm install` run? Aborting." >&2 + exit 1 +fi +VITE_BIN_WIN="$(cygpath -w "$VITE_BIN_UNIX" 2>/dev/null || printf '%s' "$VITE_BIN_UNIX")" +BEFORE_DEV_CMD="${VITE_BIN_WIN//\\/\\\\}" CONFIG_OVERRIDE="{\"build\":{\"beforeDevCommand\":\"$BEFORE_DEV_CMD\"" if (( DEV_PORT != 1420 )); then echo "[run-dev-win] OPENHUMAN_DEV_PORT=$DEV_PORT — overriding tauri devUrl" From e96fd251a5ee3f68a00e32d88dc2caf95ba509a6 Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Thu, 21 May 2026 16:47:31 +0530 Subject: [PATCH 09/17] fix(run-dev-win): wrap beforeDevCommand in space-free .bat shim When the workspace path contains spaces (e.g. `E:\Office Files\...`), pointing tauri's beforeDevCommand at the absolute `vite.CMD` directly also fails. cargo-tauri spawns the command via `cmd.exe /S /C `, and Rust's argv escaping strips any literal double-quotes around the path. cmd then parses the first space as a token boundary and tries to execute `E:\Office`. 8.3 short-name conversion (`cygpath -ws`) doesn't help when 8dot3name is disabled on the drive, which is the case on this workspace's E: drive. Write a tiny `run-vite.bat` to a space-free TEMP location and invoke that. The wrapper internally quotes the real vite.CMD path (`call "" %*`), so the spacey path is contained where we control the quoting and isn't subject to cargo-tauri's argv escaping. --- scripts/run-dev-win.sh | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/scripts/run-dev-win.sh b/scripts/run-dev-win.sh index 72f21b0351..a0787aef5f 100644 --- a/scripts/run-dev-win.sh +++ b/scripts/run-dev-win.sh @@ -620,26 +620,42 @@ fi # between bash → cargo-tauri → cmd. The default in tauri.conf.json # is `"pnpm run dev"` (bare name) which depends on PATH. # - overrides `devUrl` when OPENHUMAN_DEV_PORT is non-default. -# Point beforeDevCommand at vite directly, not through `pnpm run dev`. +# Point beforeDevCommand at vite via a wrapper batch file in a +# space-free temp directory. # -# cargo-tauri runs the beforeDevCommand from the tauri project dir -# (`app/src-tauri/`), so any `pnpm run dev` invocation inherits that -# cwd. Pnpm then prepends `./node_modules/.bin` to PATH for the script -# child — but `./` resolves against the inherited cwd, i.e. -# `app/src-tauri/node_modules/.bin`, which doesn't exist. The result is -# "'vite' is not recognized" inside cmd. +# Why a wrapper instead of the absolute path directly: +# cargo-tauri runs beforeDevCommand as `cmd.exe /S /C `. Rust's +# argv-to-cmd argument escaping strips literal double-quotes from the +# string, so if our `` is `"E:\Office Files\…\vite.CMD"`, +# cmd ends up parsing `E:\Office` as the program name and the rest as +# arguments — "'E:\Office' is not recognized". 8.3 short-name fallback +# also fails when 8dot3name is disabled on the drive (as it is on this +# workspace's E: drive). # -# Skip pnpm altogether and reference the vite shim via its absolute -# Windows path. Same effect as `pnpm run dev`, no path-resolution -# dependency on whichever cwd cargo-tauri chose. +# The fix is to call the spacey path from INSIDE a .bat file, where we +# can quote it however we want without involving cargo-tauri's outer +# escaping. The wrapper lives under %TEMP% (which is normally +# space-free) so its own path doesn't need quoting either. VITE_BIN_UNIX="$APP_DIR/node_modules/.bin/vite.CMD" if [[ ! -f "$VITE_BIN_UNIX" ]]; then echo "[run-dev-win] vite shim not found at $VITE_BIN_UNIX" >&2 - echo "[run-dev-win] Did `pnpm install` run? Aborting." >&2 + echo "[run-dev-win] Did 'pnpm install' run? Aborting." >&2 exit 1 fi VITE_BIN_WIN="$(cygpath -w "$VITE_BIN_UNIX" 2>/dev/null || printf '%s' "$VITE_BIN_UNIX")" -BEFORE_DEV_CMD="${VITE_BIN_WIN//\\/\\\\}" + +WRAPPER_DIR_UNIX="$(cygpath -u "${TEMP:-${TMP:-/tmp}}" 2>/dev/null || echo /tmp)/openhuman-dev" +mkdir -p "$WRAPPER_DIR_UNIX" +VITE_WRAPPER_UNIX="$WRAPPER_DIR_UNIX/run-vite.bat" +# Single-quoted heredoc keeps the .bat body literal (no MSYS path +# rewrites, no variable expansion). +{ + printf '@echo off\r\n' + printf 'call "%s" %%*\r\n' "$VITE_BIN_WIN" +} > "$VITE_WRAPPER_UNIX" +VITE_WRAPPER_WIN="$(cygpath -w "$VITE_WRAPPER_UNIX" 2>/dev/null || printf '%s' "$VITE_WRAPPER_UNIX")" +echo "[run-dev-win] vite wrapper at: $VITE_WRAPPER_WIN" +BEFORE_DEV_CMD="${VITE_WRAPPER_WIN//\\/\\\\}" CONFIG_OVERRIDE="{\"build\":{\"beforeDevCommand\":\"$BEFORE_DEV_CMD\"" if (( DEV_PORT != 1420 )); then echo "[run-dev-win] OPENHUMAN_DEV_PORT=$DEV_PORT — overriding tauri devUrl" From 68aa44e78e9b3b0f6392199b5feeb345031de089 Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Thu, 21 May 2026 16:50:05 +0530 Subject: [PATCH 10/17] fix(run-dev-win): wrapper bat invokes node.exe + vite.js directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous wrapper called `vite.CMD`, which in turn falls back to bare `node` when its sibling node.exe doesn't exist (the workspace's copy doesn't). cargo-tauri's cmd child for beforeDevCommand has a sanitized PATH that doesn't include node, so the fallback then fails with "'node' is not recognized". Bypass the npm-shim layer entirely: resolve `node.exe` once at the parent bash level and write a wrapper that runs "" "" %* This makes the wrapper hermetic — neither node nor the vite shim need to be on the cmd child's PATH. --- scripts/run-dev-win.sh | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/scripts/run-dev-win.sh b/scripts/run-dev-win.sh index a0787aef5f..78a2248cb8 100644 --- a/scripts/run-dev-win.sh +++ b/scripts/run-dev-win.sh @@ -636,22 +636,32 @@ fi # can quote it however we want without involving cargo-tauri's outer # escaping. The wrapper lives under %TEMP% (which is normally # space-free) so its own path doesn't need quoting either. -VITE_BIN_UNIX="$APP_DIR/node_modules/.bin/vite.CMD" -if [[ ! -f "$VITE_BIN_UNIX" ]]; then - echo "[run-dev-win] vite shim not found at $VITE_BIN_UNIX" >&2 +VITE_JS_UNIX="$APP_DIR/node_modules/vite/bin/vite.js" +if [[ ! -f "$VITE_JS_UNIX" ]]; then + echo "[run-dev-win] vite entry not found at $VITE_JS_UNIX" >&2 echo "[run-dev-win] Did 'pnpm install' run? Aborting." >&2 exit 1 fi -VITE_BIN_WIN="$(cygpath -w "$VITE_BIN_UNIX" 2>/dev/null || printf '%s' "$VITE_BIN_UNIX")" +VITE_JS_WIN="$(cygpath -w "$VITE_JS_UNIX" 2>/dev/null || printf '%s' "$VITE_JS_UNIX")" + +# Resolve node.exe absolute path so the wrapper doesn't depend on +# whatever PATH cargo-tauri hands to its cmd child. +NODE_EXE_UNIX="$(command -v node.exe 2>/dev/null || command -v node 2>/dev/null)" +if [[ -z "$NODE_EXE_UNIX" || ! -f "$NODE_EXE_UNIX" ]]; then + echo "[run-dev-win] node.exe not findable on bash PATH at wrapper-build time" >&2 + exit 1 +fi +NODE_EXE_WIN="$(cygpath -w "$NODE_EXE_UNIX" 2>/dev/null || printf '%s' "$NODE_EXE_UNIX")" WRAPPER_DIR_UNIX="$(cygpath -u "${TEMP:-${TMP:-/tmp}}" 2>/dev/null || echo /tmp)/openhuman-dev" mkdir -p "$WRAPPER_DIR_UNIX" VITE_WRAPPER_UNIX="$WRAPPER_DIR_UNIX/run-vite.bat" -# Single-quoted heredoc keeps the .bat body literal (no MSYS path -# rewrites, no variable expansion). +# Invoke node.exe with vite's JS entry directly. The vite.CMD shim +# falls back to bare `node` when its sibling doesn't have node.exe, +# which fails inside cargo-tauri's cmd child (no node on PATH). { printf '@echo off\r\n' - printf 'call "%s" %%*\r\n' "$VITE_BIN_WIN" + printf '"%s" "%s" %%*\r\n' "$NODE_EXE_WIN" "$VITE_JS_WIN" } > "$VITE_WRAPPER_UNIX" VITE_WRAPPER_WIN="$(cygpath -w "$VITE_WRAPPER_UNIX" 2>/dev/null || printf '%s' "$VITE_WRAPPER_UNIX")" echo "[run-dev-win] vite wrapper at: $VITE_WRAPPER_WIN" From 2d74ccd56eae3321edca91c52b4f4a59c4ad7137 Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Thu, 21 May 2026 17:03:26 +0530 Subject: [PATCH 11/17] fix(tls): force native TLS on runtime proxy clients MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `build_runtime_proxy_client` and `build_runtime_proxy_client_with_timeouts` are the shared reqwest factories used by composio integrations, channel providers (discord, dingtalk, …), and the multimodal agent path. They built clients with default TLS, which in this dep configuration resolves to rustls + webpki-roots and fails with `UnknownIssuer` whenever the network presents an OS-trusted but webpki-unknown CA chain (corporate TLS interception). Add `.use_native_tls()` to both builders so the OS trust store is honored. Matches the earlier swap in `api::rest` and `openhuman::app_state` — same rationale, broader coverage. --- src/openhuman/config/schema/proxy.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/openhuman/config/schema/proxy.rs b/src/openhuman/config/schema/proxy.rs index b843d397e1..eb84ba7710 100644 --- a/src/openhuman/config/schema/proxy.rs +++ b/src/openhuman/config/schema/proxy.rs @@ -441,7 +441,13 @@ pub fn build_runtime_proxy_client(service_key: &str) -> reqwest::Client { return client; } - let builder = apply_runtime_proxy_to_builder(reqwest::Client::builder(), service_key); + // Native TLS so the OS trust store wins for outbound calls to + // api.tinyhumans.ai / composio / channel providers. See + // [`crate::api::rest`] for the broader rationale. + let builder = apply_runtime_proxy_to_builder( + reqwest::Client::builder().use_native_tls(), + service_key, + ); let client = builder.build().unwrap_or_else(|error| { tracing::warn!(service_key, "Failed to build proxied client: {error}"); reqwest::Client::new() @@ -462,6 +468,7 @@ pub fn build_runtime_proxy_client_with_timeouts( } let builder = reqwest::Client::builder() + .use_native_tls() .timeout(std::time::Duration::from_secs(timeout_secs)) .connect_timeout(std::time::Duration::from_secs(connect_timeout_secs)); let builder = apply_runtime_proxy_to_builder(builder, service_key); From e37428e6239d790b362f77fcd1e491e1bfca1a9e Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Thu, 21 May 2026 17:13:18 +0530 Subject: [PATCH 12/17] fix(tls): gate native-tls swap to Windows; preserve rustls everywhere else MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Earlier commits in this branch unconditionally swapped reqwest and tokio-tungstenite to native-tls to fix `UnknownIssuer` failures on Windows networks that intercept TLS with a corporate CA. macOS and Linux don't hit that scenario in normal distribution, and Linux in particular would gain an unwanted runtime dependency on libssl / libcrypto if it stayed on native-tls. Gate every TLS-backend selection behind `#[cfg(target_os = "windows")]`: - Windows builds resolve TLS via native-tls (schannel) — the OS cert store wins, fixing corporate-MITM environments. - macOS / Linux builds keep `use_rustls_tls()` and tokio-tungstenite's `rustls-tls-webpki-roots` — the historical behavior, no regression. Sites touched: - api::rest, openhuman::app_state — HTTP clients - openhuman::config::schema::proxy — central proxy-client factories used by composio, channels, agent paths - integrations::{client, searxng, seltz} — explicit-rustls clients - inference::provider::compatible — two TLS call sites - Cargo.toml — tokio-tungstenite split into per-target dependency entries so each platform compiles with exactly one TLS feature --- Cargo.toml | 15 +++++++++++- src/api/rest.rs | 22 +++++++++++------ src/openhuman/app_state/ops.rs | 11 ++++++--- src/openhuman/config/schema/proxy.rs | 24 +++++++++++-------- .../inference/provider/compatible.rs | 19 +++++++++++---- src/openhuman/integrations/client.rs | 12 ++++++++-- src/openhuman/integrations/searxng.rs | 10 +++++--- src/openhuman/integrations/seltz.rs | 9 +++++-- 8 files changed, 90 insertions(+), 32 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ddb3f4b9d6..547ea5b98a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,7 +76,9 @@ async-trait = "0.1" chacha20poly1305 = "0.10" hex = "0.4" tokio-util = { version = "0.7", features = ["rt", "io"] } -tokio-tungstenite = { version = "0.24", features = ["native-tls"] } +# tokio-tungstenite is declared per-target below so the TLS backend +# (native-tls on Windows, rustls on macOS / Linux) matches the reqwest +# backend selected at each TLS call site. futures = "0.3" rusqlite = { version = "0.37", features = ["bundled"] } chrono = { version = "0.4", features = ["serde"] } @@ -158,6 +160,17 @@ whatsapp-rust-tokio-transport = { version = "0.5", optional = true, default-feat whatsapp-rust-ureq-http-client = { version = "0.5", optional = true } wacore = { version = "0.5", optional = true, default-features = false } +[target.'cfg(windows)'.dependencies] +# Windows: tokio-tungstenite uses native-tls (schannel) so wss:// +# connections honor the Windows cert store, including corporate CAs +# installed by AV / TLS-inspection proxies. See run-dev-win.sh notes. +tokio-tungstenite = { version = "0.24", default-features = false, features = ["connect", "handshake", "native-tls"] } + +[target.'cfg(not(windows))'.dependencies] +# macOS / Linux: keep rustls + Mozilla webpki-roots — the historical +# default. Avoids pulling OpenSSL as a runtime dep on Linux. +tokio-tungstenite = { version = "0.24", default-features = false, features = ["connect", "handshake", "rustls-tls-webpki-roots"] } + [target.'cfg(target_os = "macos")'.dependencies] whisper-rs = { version = "0.16", features = ["metal"] } # Contacts framework bindings for address book seeding. diff --git a/src/api/rest.rs b/src/api/rest.rs index 99134e83bd..cca1e9a98b 100644 --- a/src/api/rest.rs +++ b/src/api/rest.rs @@ -78,13 +78,21 @@ fn build_backend_reqwest_client() -> Result { ); } - // Use the platform TLS stack (schannel/SecureTransport/OpenSSL) so the - // OS trust store is honored — needed when running behind corporate - // TLS-inspecting proxies that present a re-signed cert from a CA only - // trusted by the OS, not by rustls's bundled webpki-roots. - Client::builder() - .default_headers(default_headers) - .use_native_tls() + // TLS backend selection: + // - Windows: schannel (native-tls) so the Windows cert store is + // honored, including any corporate CA installed by AV / TLS- + // inspecting proxies that re-sign certs with a private root. + // rustls + webpki-roots only knows Mozilla CAs and fails such + // environments with `UnknownIssuer`. + // - macOS / Linux: keep rustls + webpki-roots — these platforms + // don't hit the corporate-MITM scenario in normal distribution, + // and rustls avoids the OpenSSL runtime dependency on Linux. + let builder = Client::builder().default_headers(default_headers); + #[cfg(target_os = "windows")] + let builder = builder.use_native_tls(); + #[cfg(not(target_os = "windows"))] + let builder = builder.use_rustls_tls(); + builder .http1_only() .timeout(Duration::from_secs(120)) .connect_timeout(Duration::from_secs(15)) diff --git a/src/openhuman/app_state/ops.rs b/src/openhuman/app_state/ops.rs index 0af1425fce..c8fd7808e3 100644 --- a/src/openhuman/app_state/ops.rs +++ b/src/openhuman/app_state/ops.rs @@ -263,9 +263,14 @@ fn save_stored_app_state(config: &Config, state: &StoredAppState) -> Result<(), } fn build_client() -> Result { - // Native TLS so the OS trust store is honored — see [`crate::api::rest`]. - Client::builder() - .use_native_tls() + // Windows: native TLS so the OS trust store is honored — see + // [`crate::api::rest`]. Other platforms keep rustls. + let builder = Client::builder(); + #[cfg(target_os = "windows")] + let builder = builder.use_native_tls(); + #[cfg(not(target_os = "windows"))] + let builder = builder.use_rustls_tls(); + builder .http1_only() .timeout(Duration::from_secs(30)) .connect_timeout(Duration::from_secs(10)) diff --git a/src/openhuman/config/schema/proxy.rs b/src/openhuman/config/schema/proxy.rs index eb84ba7710..f90c10c8f0 100644 --- a/src/openhuman/config/schema/proxy.rs +++ b/src/openhuman/config/schema/proxy.rs @@ -441,13 +441,14 @@ pub fn build_runtime_proxy_client(service_key: &str) -> reqwest::Client { return client; } - // Native TLS so the OS trust store wins for outbound calls to - // api.tinyhumans.ai / composio / channel providers. See - // [`crate::api::rest`] for the broader rationale. - let builder = apply_runtime_proxy_to_builder( - reqwest::Client::builder().use_native_tls(), - service_key, - ); + // TLS backend: Windows uses native (schannel) so the OS cert store + // is honored — see [`crate::api::rest`]. macOS / Linux keep rustls. + let raw = reqwest::Client::builder(); + #[cfg(target_os = "windows")] + let raw = raw.use_native_tls(); + #[cfg(not(target_os = "windows"))] + let raw = raw.use_rustls_tls(); + let builder = apply_runtime_proxy_to_builder(raw, service_key); let client = builder.build().unwrap_or_else(|error| { tracing::warn!(service_key, "Failed to build proxied client: {error}"); reqwest::Client::new() @@ -467,11 +468,14 @@ pub fn build_runtime_proxy_client_with_timeouts( return client; } - let builder = reqwest::Client::builder() - .use_native_tls() + let raw = reqwest::Client::builder() .timeout(std::time::Duration::from_secs(timeout_secs)) .connect_timeout(std::time::Duration::from_secs(connect_timeout_secs)); - let builder = apply_runtime_proxy_to_builder(builder, service_key); + #[cfg(target_os = "windows")] + let raw = raw.use_native_tls(); + #[cfg(not(target_os = "windows"))] + let raw = raw.use_rustls_tls(); + let builder = apply_runtime_proxy_to_builder(raw, service_key); let client = builder.build().unwrap_or_else(|error| { tracing::warn!( service_key, diff --git a/src/openhuman/inference/provider/compatible.rs b/src/openhuman/inference/provider/compatible.rs index d17bf8dd17..80a6150ad2 100644 --- a/src/openhuman/inference/provider/compatible.rs +++ b/src/openhuman/inference/provider/compatible.rs @@ -278,8 +278,14 @@ impl OpenAiCompatibleProvider { headers.insert(USER_AGENT, value); } - let builder = Client::builder() - .use_rustls_tls() + // Windows: native TLS so the OS cert store wins. Other + // platforms keep rustls. + let builder = Client::builder(); + #[cfg(target_os = "windows")] + let builder = builder.use_native_tls(); + #[cfg(not(target_os = "windows"))] + let builder = builder.use_rustls_tls(); + let builder = builder .timeout(std::time::Duration::from_secs(120)) .connect_timeout(std::time::Duration::from_secs(10)) .default_headers(headers); @@ -294,8 +300,13 @@ impl OpenAiCompatibleProvider { }); } - let builder = Client::builder() - .use_rustls_tls() + // Windows: native TLS for OS cert store; else: keep rustls. + let builder = Client::builder(); + #[cfg(target_os = "windows")] + let builder = builder.use_native_tls(); + #[cfg(not(target_os = "windows"))] + let builder = builder.use_rustls_tls(); + let builder = builder .timeout(std::time::Duration::from_secs(120)) .connect_timeout(std::time::Duration::from_secs(10)); let builder = crate::openhuman::config::apply_runtime_proxy_to_builder( diff --git a/src/openhuman/integrations/client.rs b/src/openhuman/integrations/client.rs index 1b35468162..15bd4386bd 100644 --- a/src/openhuman/integrations/client.rs +++ b/src/openhuman/integrations/client.rs @@ -100,8 +100,16 @@ impl IntegrationClient { // has historically failed on staging TLS handshakes while // rustls succeeds — so the integrations client was the odd one // out with raw "error sending request" failures. - let http_client = reqwest::Client::builder() - .use_rustls_tls() + // Windows: native TLS (schannel) so the OS cert store is honored + // — needed when corporate TLS interception re-signs certs with a + // private CA. macOS / Linux: keep rustls (preserves the staging + // handshake compatibility documented above). + let http_builder = reqwest::Client::builder(); + #[cfg(target_os = "windows")] + let http_builder = http_builder.use_native_tls(); + #[cfg(not(target_os = "windows"))] + let http_builder = http_builder.use_rustls_tls(); + let http_client = http_builder .http1_only() .timeout(Duration::from_secs(60)) .connect_timeout(Duration::from_secs(15)) diff --git a/src/openhuman/integrations/searxng.rs b/src/openhuman/integrations/searxng.rs index 6eb878b147..9a86775f83 100644 --- a/src/openhuman/integrations/searxng.rs +++ b/src/openhuman/integrations/searxng.rs @@ -23,9 +23,13 @@ fn shared_http_client() -> reqwest::Client { SHARED_HTTP_CLIENT .get_or_init(|| { tracing::debug!("[searxng] initializing shared HTTP client"); - reqwest::Client::builder() - .use_rustls_tls() - .build() + // Windows: native TLS for OS cert store; else: keep rustls. + let b = reqwest::Client::builder(); + #[cfg(target_os = "windows")] + let b = b.use_native_tls(); + #[cfg(not(target_os = "windows"))] + let b = b.use_rustls_tls(); + b.build() .expect("failed to build shared SearXNG HTTP client") }) .clone() diff --git a/src/openhuman/integrations/seltz.rs b/src/openhuman/integrations/seltz.rs index 22c12a48ee..ce6b390307 100644 --- a/src/openhuman/integrations/seltz.rs +++ b/src/openhuman/integrations/seltz.rs @@ -64,8 +64,13 @@ impl SeltzSearchTool { timeout_secs: u64, ) -> Self { let timeout = timeout_secs.max(1); - let http_client = reqwest::Client::builder() - .use_rustls_tls() + // Windows: native TLS for OS cert store; else: keep rustls. + let http_builder = reqwest::Client::builder(); + #[cfg(target_os = "windows")] + let http_builder = http_builder.use_native_tls(); + #[cfg(not(target_os = "windows"))] + let http_builder = http_builder.use_rustls_tls(); + let http_client = http_builder .http1_only() .timeout(Duration::from_secs(timeout)) .connect_timeout(Duration::from_secs(10)) From 8b179199c878a2c3d5a96c5cfb209c3d87534afa Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Thu, 21 May 2026 18:48:27 +0530 Subject: [PATCH 13/17] chore: regenerate Cargo.lock after tokio-tungstenite feature swap Per-target `tokio-tungstenite` dependency entries (see earlier `fix(tls): gate native-tls swap to Windows` commit) introduced `native-tls` and `tokio-native-tls` as transitive deps. Refresh both the root and Tauri-shell lock files so the resolved graphs stay consistent with the manifests. --- Cargo.lock | 3 +++ app/src-tauri/Cargo.lock | 54 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 1f8818db28..b498623341 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7734,9 +7734,11 @@ checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" dependencies = [ "futures-util", "log", + "native-tls", "rustls", "rustls-pki-types", "tokio", + "tokio-native-tls", "tokio-rustls", "tungstenite 0.24.0", "webpki-roots 0.26.11", @@ -8010,6 +8012,7 @@ dependencies = [ "http 1.4.0", "httparse", "log", + "native-tls", "rand 0.8.6", "rustls", "rustls-pki-types", diff --git a/app/src-tauri/Cargo.lock b/app/src-tauri/Cargo.lock index 54350c9523..eadcf48845 100644 --- a/app/src-tauri/Cargo.lock +++ b/app/src-tauri/Cargo.lock @@ -2114,6 +2114,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "endi" version = "1.1.1" @@ -3384,9 +3393,11 @@ dependencies = [ "percent-encoding", "pin-project-lite", "socket2", + "system-configuration", "tokio", "tower-service", "tracing", + "windows-registry", ] [[package]] @@ -4309,6 +4320,22 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "motosan-ai-oauth" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16994a67367076b08479af83ca05503c4d423fc6631f849fb92fa787956ad557" +dependencies = [ + "base64 0.22.1", + "percent-encoding", + "rand 0.9.4", + "reqwest 0.12.28", + "serde", + "sha2 0.10.9", + "thiserror 2.0.18", + "tokio", +] + [[package]] name = "moxcms" version = "0.8.1" @@ -5064,6 +5091,7 @@ dependencies = [ "lettre", "log", "mail-parser", + "motosan-ai-oauth", "nu-ansi-term 0.46.0", "objc2 0.6.4", "objc2-contacts", @@ -6363,6 +6391,7 @@ checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ "base64 0.22.1", "bytes", + "encoding_rs", "futures-channel", "futures-core", "futures-util", @@ -6376,6 +6405,7 @@ dependencies = [ "hyper-util", "js-sys", "log", + "mime", "mime_guess", "native-tls", "percent-encoding", @@ -7714,6 +7744,27 @@ dependencies = [ "windows 0.57.0", ] +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags 2.11.1", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys 0.8.7", + "libc", +] + [[package]] name = "system-deps" version = "6.2.2" @@ -8452,9 +8503,11 @@ checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" dependencies = [ "futures-util", "log", + "native-tls", "rustls", "rustls-pki-types", "tokio", + "tokio-native-tls", "tokio-rustls", "tungstenite 0.24.0", "webpki-roots 0.26.11", @@ -8772,6 +8825,7 @@ dependencies = [ "http", "httparse", "log", + "native-tls", "rand 0.8.6", "rustls", "rustls-pki-types", From 456d4ef04973425d43142a73146ed281c25f83ec Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Thu, 21 May 2026 19:14:13 +0530 Subject: [PATCH 14/17] fix(run-dev-win): guard against spaces in TEMP-derived vite wrapper path If %TEMP% contains spaces the unquoted .bat path would cause cmd.exe to split on the space and fail to start Vite. Detect the condition early and exit with a clear message directing the user to set TEMP to a space-free path (e.g. C:\Temp). --- scripts/run-dev-win.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/run-dev-win.sh b/scripts/run-dev-win.sh index abe4ee2662..8dd6c1714f 100644 --- a/scripts/run-dev-win.sh +++ b/scripts/run-dev-win.sh @@ -687,6 +687,11 @@ VITE_WRAPPER_UNIX="$WRAPPER_DIR_UNIX/run-vite.bat" printf '"%s" "%s" %%*\r\n' "$NODE_EXE_WIN" "$VITE_JS_WIN" } > "$VITE_WRAPPER_UNIX" VITE_WRAPPER_WIN="$(cygpath -w "$VITE_WRAPPER_UNIX" 2>/dev/null || printf '%s' "$VITE_WRAPPER_UNIX")" +if [[ "$VITE_WRAPPER_WIN" == *" "* ]]; then + echo "[run-dev-win] wrapper path contains spaces: $VITE_WRAPPER_WIN" >&2 + echo "[run-dev-win] set TEMP/TMP to a space-free path (e.g. C:\\Temp) and retry." >&2 + exit 1 +fi echo "[run-dev-win] vite wrapper at: $VITE_WRAPPER_WIN" BEFORE_DEV_CMD="${VITE_WRAPPER_WIN//\\/\\\\}" CONFIG_OVERRIDE="{\"build\":{\"beforeDevCommand\":\"$BEFORE_DEV_CMD\"" From be9b0d7747d3406c8c327254b3e7f8a3821b0cb7 Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Thu, 21 May 2026 23:15:47 +0530 Subject: [PATCH 15/17] refactor(tls): extract shared tls_client_builder() helper; fix fallback TLS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses @M3gA-Mind review comments: - Add src/openhuman/tls.rs with tls_client_builder() — the single source of truth for platform-conditional TLS selection (Windows → native-tls / schannel, macOS+Linux → rustls). All 8 previous copy-paste sites now delegate to this helper (rest.rs, ops.rs, proxy.rs ×2, compatible.rs ×2, client.rs, searxng.rs, seltz.rs). - Fix proxy.rs: Client::new() fallback in both build_runtime_proxy_client and build_runtime_proxy_client_with_timeouts was silently reverting to rustls on Windows when the main builder failed. Both error paths now call tls_client_builder() so the fallback client honors the OS cert store too (addresses @M3gA-Mind on src/openhuman/config/schema/proxy.rs:454,480). - Add historical PATH-dedup comment to scripts/run-dev-win.sh explaining why the old dedup loop was removed — preserves institutional knowledge about the MSYS env-block overflow that previously caused empty-PATH children (addresses @M3gA-Mind on scripts/run-dev-win.sh:645). --- scripts/run-dev-win.sh | 9 +++++ src/api/rest.rs | 20 +++-------- src/openhuman/app_state/ops.rs | 10 ++---- src/openhuman/config/schema/proxy.rs | 28 +++++++-------- .../inference/provider/compatible.rs | 19 +++-------- src/openhuman/integrations/client.rs | 23 +++---------- src/openhuman/integrations/searxng.rs | 10 ++---- src/openhuman/integrations/seltz.rs | 9 ++--- src/openhuman/mod.rs | 1 + src/openhuman/tls.rs | 34 +++++++++++++++++++ 10 files changed, 78 insertions(+), 85 deletions(-) create mode 100644 src/openhuman/tls.rs diff --git a/scripts/run-dev-win.sh b/scripts/run-dev-win.sh index 8dd6c1714f..d30c716fc9 100644 --- a/scripts/run-dev-win.sh +++ b/scripts/run-dev-win.sh @@ -630,6 +630,15 @@ fi # `ensure-tauri-cli.sh` already installed the vendored CEF-aware # cargo-tauri at `$REPO_ROOT/.cache/cargo-install/bin/cargo-tauri.exe`, # so we can invoke that binary directly and skip the wrapper layer. +# +# Historical note: a previous version of this script ran a PATH +# deduplication loop (collapsing repeated entries that MSYS→Windows +# conversion stacked during vcvars / Git-Bash re-runs / pnpm layering). +# That loop was needed because the overflowing env block left child +# processes with an EMPTY PATH — even `where.exe` was gone, causing +# "'pnpm' is not recognized". Direct cargo-tauri.exe invocation with +# absolute paths in the .bat wrapper makes the env block size irrelevant: +# beforeDevCommand no longer needs PATH at all. CARGO_TAURI_EXE="$REPO_ROOT/.cache/cargo-install/bin/cargo-tauri.exe" if [[ ! -x "$CARGO_TAURI_EXE" ]]; then echo "[run-dev-win] cargo-tauri.exe not found at $CARGO_TAURI_EXE" >&2 diff --git a/src/api/rest.rs b/src/api/rest.rs index cca1e9a98b..74bf02c6d7 100644 --- a/src/api/rest.rs +++ b/src/api/rest.rs @@ -78,21 +78,11 @@ fn build_backend_reqwest_client() -> Result { ); } - // TLS backend selection: - // - Windows: schannel (native-tls) so the Windows cert store is - // honored, including any corporate CA installed by AV / TLS- - // inspecting proxies that re-sign certs with a private root. - // rustls + webpki-roots only knows Mozilla CAs and fails such - // environments with `UnknownIssuer`. - // - macOS / Linux: keep rustls + webpki-roots — these platforms - // don't hit the corporate-MITM scenario in normal distribution, - // and rustls avoids the OpenSSL runtime dependency on Linux. - let builder = Client::builder().default_headers(default_headers); - #[cfg(target_os = "windows")] - let builder = builder.use_native_tls(); - #[cfg(not(target_os = "windows"))] - let builder = builder.use_rustls_tls(); - builder + // Platform-appropriate TLS backend: Windows → schannel (honors the OS + // cert store, required for corporate TLS-inspection proxies); macOS / + // Linux → rustls. See [`crate::openhuman::tls::tls_client_builder`]. + crate::openhuman::tls::tls_client_builder() + .default_headers(default_headers) .http1_only() .timeout(Duration::from_secs(120)) .connect_timeout(Duration::from_secs(15)) diff --git a/src/openhuman/app_state/ops.rs b/src/openhuman/app_state/ops.rs index c8fd7808e3..cd609b9c06 100644 --- a/src/openhuman/app_state/ops.rs +++ b/src/openhuman/app_state/ops.rs @@ -263,14 +263,8 @@ fn save_stored_app_state(config: &Config, state: &StoredAppState) -> Result<(), } fn build_client() -> Result { - // Windows: native TLS so the OS trust store is honored — see - // [`crate::api::rest`]. Other platforms keep rustls. - let builder = Client::builder(); - #[cfg(target_os = "windows")] - let builder = builder.use_native_tls(); - #[cfg(not(target_os = "windows"))] - let builder = builder.use_rustls_tls(); - builder + // Platform-appropriate TLS backend — see [`crate::openhuman::tls`]. + crate::openhuman::tls::tls_client_builder() .http1_only() .timeout(Duration::from_secs(30)) .connect_timeout(Duration::from_secs(10)) diff --git a/src/openhuman/config/schema/proxy.rs b/src/openhuman/config/schema/proxy.rs index f90c10c8f0..c3f874b6ca 100644 --- a/src/openhuman/config/schema/proxy.rs +++ b/src/openhuman/config/schema/proxy.rs @@ -441,17 +441,15 @@ pub fn build_runtime_proxy_client(service_key: &str) -> reqwest::Client { return client; } - // TLS backend: Windows uses native (schannel) so the OS cert store - // is honored — see [`crate::api::rest`]. macOS / Linux keep rustls. - let raw = reqwest::Client::builder(); - #[cfg(target_os = "windows")] - let raw = raw.use_native_tls(); - #[cfg(not(target_os = "windows"))] - let raw = raw.use_rustls_tls(); - let builder = apply_runtime_proxy_to_builder(raw, service_key); + // Platform-appropriate TLS backend — see [`crate::openhuman::tls`]. + let builder = + apply_runtime_proxy_to_builder(crate::openhuman::tls::tls_client_builder(), service_key); let client = builder.build().unwrap_or_else(|error| { tracing::warn!(service_key, "Failed to build proxied client: {error}"); - reqwest::Client::new() + // Apply the same platform TLS selection on the fallback path so the + // error-path client also honors the Windows cert store. + let fb = crate::openhuman::tls::tls_client_builder(); + fb.build().unwrap_or_default() }); set_runtime_proxy_cached_client(cache_key, client.clone()); client @@ -468,20 +466,20 @@ pub fn build_runtime_proxy_client_with_timeouts( return client; } - let raw = reqwest::Client::builder() + // Platform-appropriate TLS backend — see [`crate::openhuman::tls`]. + let raw = crate::openhuman::tls::tls_client_builder() .timeout(std::time::Duration::from_secs(timeout_secs)) .connect_timeout(std::time::Duration::from_secs(connect_timeout_secs)); - #[cfg(target_os = "windows")] - let raw = raw.use_native_tls(); - #[cfg(not(target_os = "windows"))] - let raw = raw.use_rustls_tls(); let builder = apply_runtime_proxy_to_builder(raw, service_key); let client = builder.build().unwrap_or_else(|error| { tracing::warn!( service_key, "Failed to build proxied timeout client: {error}" ); - reqwest::Client::new() + // Apply the same platform TLS selection on the fallback path so the + // error-path client also honors the Windows cert store. + let fb = crate::openhuman::tls::tls_client_builder(); + fb.build().unwrap_or_default() }); set_runtime_proxy_cached_client(cache_key, client.clone()); client diff --git a/src/openhuman/inference/provider/compatible.rs b/src/openhuman/inference/provider/compatible.rs index 80a6150ad2..6094b8a110 100644 --- a/src/openhuman/inference/provider/compatible.rs +++ b/src/openhuman/inference/provider/compatible.rs @@ -278,14 +278,8 @@ impl OpenAiCompatibleProvider { headers.insert(USER_AGENT, value); } - // Windows: native TLS so the OS cert store wins. Other - // platforms keep rustls. - let builder = Client::builder(); - #[cfg(target_os = "windows")] - let builder = builder.use_native_tls(); - #[cfg(not(target_os = "windows"))] - let builder = builder.use_rustls_tls(); - let builder = builder + // Platform-appropriate TLS backend — see [`crate::openhuman::tls`]. + let builder = crate::openhuman::tls::tls_client_builder() .timeout(std::time::Duration::from_secs(120)) .connect_timeout(std::time::Duration::from_secs(10)) .default_headers(headers); @@ -300,13 +294,8 @@ impl OpenAiCompatibleProvider { }); } - // Windows: native TLS for OS cert store; else: keep rustls. - let builder = Client::builder(); - #[cfg(target_os = "windows")] - let builder = builder.use_native_tls(); - #[cfg(not(target_os = "windows"))] - let builder = builder.use_rustls_tls(); - let builder = builder + // Platform-appropriate TLS backend — see [`crate::openhuman::tls`]. + let builder = crate::openhuman::tls::tls_client_builder() .timeout(std::time::Duration::from_secs(120)) .connect_timeout(std::time::Duration::from_secs(10)); let builder = crate::openhuman::config::apply_runtime_proxy_to_builder( diff --git a/src/openhuman/integrations/client.rs b/src/openhuman/integrations/client.rs index 15bd4386bd..ab77f8d57f 100644 --- a/src/openhuman/integrations/client.rs +++ b/src/openhuman/integrations/client.rs @@ -92,24 +92,11 @@ impl IntegrationClient { // to fix up the input so the regression is observable in logs. let backend_url = sanitize_backend_url(&backend_url); - // Match the TLS config used by `BackendOAuthClient` in - // `src/api/rest.rs`: force rustls + HTTP/1.1 so we get the same - // consistent cross-platform behaviour every other backend-proxied - // domain (billing, team, webhooks, referral, …) already relies - // on. The default builder picks up native-tls on macOS, which - // has historically failed on staging TLS handshakes while - // rustls succeeds — so the integrations client was the odd one - // out with raw "error sending request" failures. - // Windows: native TLS (schannel) so the OS cert store is honored - // — needed when corporate TLS interception re-signs certs with a - // private CA. macOS / Linux: keep rustls (preserves the staging - // handshake compatibility documented above). - let http_builder = reqwest::Client::builder(); - #[cfg(target_os = "windows")] - let http_builder = http_builder.use_native_tls(); - #[cfg(not(target_os = "windows"))] - let http_builder = http_builder.use_rustls_tls(); - let http_client = http_builder + // Platform-appropriate TLS backend — see [`crate::openhuman::tls`]. + // Windows uses schannel (native-tls) to honor the OS cert store; + // macOS / Linux keep rustls which avoids the OpenSSL runtime dep and + // has historically been more reliable on staging TLS handshakes. + let http_client = crate::openhuman::tls::tls_client_builder() .http1_only() .timeout(Duration::from_secs(60)) .connect_timeout(Duration::from_secs(15)) diff --git a/src/openhuman/integrations/searxng.rs b/src/openhuman/integrations/searxng.rs index 9a86775f83..4f76499461 100644 --- a/src/openhuman/integrations/searxng.rs +++ b/src/openhuman/integrations/searxng.rs @@ -23,13 +23,9 @@ fn shared_http_client() -> reqwest::Client { SHARED_HTTP_CLIENT .get_or_init(|| { tracing::debug!("[searxng] initializing shared HTTP client"); - // Windows: native TLS for OS cert store; else: keep rustls. - let b = reqwest::Client::builder(); - #[cfg(target_os = "windows")] - let b = b.use_native_tls(); - #[cfg(not(target_os = "windows"))] - let b = b.use_rustls_tls(); - b.build() + // Platform-appropriate TLS backend — see [`crate::openhuman::tls`]. + crate::openhuman::tls::tls_client_builder() + .build() .expect("failed to build shared SearXNG HTTP client") }) .clone() diff --git a/src/openhuman/integrations/seltz.rs b/src/openhuman/integrations/seltz.rs index ce6b390307..a9e4b4a607 100644 --- a/src/openhuman/integrations/seltz.rs +++ b/src/openhuman/integrations/seltz.rs @@ -64,13 +64,8 @@ impl SeltzSearchTool { timeout_secs: u64, ) -> Self { let timeout = timeout_secs.max(1); - // Windows: native TLS for OS cert store; else: keep rustls. - let http_builder = reqwest::Client::builder(); - #[cfg(target_os = "windows")] - let http_builder = http_builder.use_native_tls(); - #[cfg(not(target_os = "windows"))] - let http_builder = http_builder.use_rustls_tls(); - let http_client = http_builder + // Platform-appropriate TLS backend — see [`crate::openhuman::tls`]. + let http_client = crate::openhuman::tls::tls_client_builder() .http1_only() .timeout(Duration::from_secs(timeout)) .connect_timeout(Duration::from_secs(10)) diff --git a/src/openhuman/mod.rs b/src/openhuman/mod.rs index 5c081d3c7c..6fc21db601 100644 --- a/src/openhuman/mod.rs +++ b/src/openhuman/mod.rs @@ -74,6 +74,7 @@ pub mod team; pub mod test_support; pub mod text_input; pub mod threads; +pub mod tls; pub mod todos; pub mod tokenjuice; pub mod tool_registry; diff --git a/src/openhuman/tls.rs b/src/openhuman/tls.rs new file mode 100644 index 0000000000..cb9a644424 --- /dev/null +++ b/src/openhuman/tls.rs @@ -0,0 +1,34 @@ +//! Platform-conditional TLS backend selection for reqwest clients. +//! +//! Centralises the `#[cfg(target_os = "windows")]` / `#[cfg(not(target_os = "windows"))]` +//! guard so every HTTP-client construction site stays at one line and future +//! policy changes (e.g. adding native-tls on macOS) only require editing this file. +//! +//! # Policy +//! - **Windows**: `native-tls` (schannel) — honors the Windows certificate store, +//! including any corporate CA installed by AV / TLS-inspecting proxies that +//! re-sign certificates with a private root. `rustls` + webpki-roots only knows +//! Mozilla CAs and fails such environments with `UnknownIssuer`. +//! - **macOS / Linux**: `rustls` + webpki-roots — avoids the OpenSSL runtime +//! dependency on Linux and has historically been more reliable on macOS staging +//! TLS handshakes than `native-tls`. + +/// Return a `reqwest::ClientBuilder` pre-configured with the platform-appropriate +/// TLS backend. +/// +/// Use this as the starting point for every client that needs to reach external +/// HTTPS endpoints: +/// ```rust,ignore +/// let client = tls_client_builder() +/// .http1_only() +/// .timeout(Duration::from_secs(30)) +/// .build()?; +/// ``` +pub fn tls_client_builder() -> reqwest::ClientBuilder { + let b = reqwest::Client::builder(); + #[cfg(target_os = "windows")] + let b = b.use_native_tls(); + #[cfg(not(target_os = "windows"))] + let b = b.use_rustls_tls(); + b +} From 01ad00332204b888e61932e035e07252d305e502 Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Thu, 21 May 2026 23:17:58 +0530 Subject: [PATCH 16/17] fix(tls): apply platform TLS on Client::new() fallback paths in compatible.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two .unwrap_or_else fallbacks in OpenAiCompatibleProvider::http_client() were using Client::new() which silently picks rustls on Windows when both native-tls and rustls features are compiled in. Replace with tls_client_builder().build().unwrap_or_default() so the error-path client also honors the Windows cert store — same fix already applied to proxy.rs. --- src/openhuman/inference/provider/compatible.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/openhuman/inference/provider/compatible.rs b/src/openhuman/inference/provider/compatible.rs index 6094b8a110..52be1acf26 100644 --- a/src/openhuman/inference/provider/compatible.rs +++ b/src/openhuman/inference/provider/compatible.rs @@ -290,7 +290,9 @@ impl OpenAiCompatibleProvider { return builder.build().unwrap_or_else(|error| { tracing::warn!("Failed to build proxied timeout client with user-agent: {error}"); - Client::new() + crate::openhuman::tls::tls_client_builder() + .build() + .unwrap_or_default() }); } @@ -304,7 +306,9 @@ impl OpenAiCompatibleProvider { ); builder.build().unwrap_or_else(|error| { tracing::warn!("Failed to build proxied timeout client: {error}"); - Client::new() + crate::openhuman::tls::tls_client_builder() + .build() + .unwrap_or_default() }) } From 8bf383f8532c7e5ea5d88a8b9b76da117207dbbe Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Thu, 21 May 2026 23:25:22 +0530 Subject: [PATCH 17/17] fix(tls): apply platform TLS to composio DELETE client and proxy timeout fallback - composio/client.rs: replace hardcoded use_rustls_tls() with tls_client_builder() so the DELETE helper client picks up native-tls on Windows, matching the shared IntegrationClient it mirrors (addresses @graycyrus on src/openhuman/composio/client.rs:462) - proxy.rs: propagate timeout_secs and connect_timeout_secs into the tls_client_builder() fallback in build_runtime_proxy_client_with_timeouts so the error-path client remains bounded and honors the same timeouts as the primary builder (addresses @coderabbitai on src/openhuman/config/schema/proxy.rs:483) --- src/openhuman/composio/client.rs | 9 ++++----- src/openhuman/config/schema/proxy.rs | 9 ++++++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/openhuman/composio/client.rs b/src/openhuman/composio/client.rs index 27d3550674..d55a805016 100644 --- a/src/openhuman/composio/client.rs +++ b/src/openhuman/composio/client.rs @@ -455,11 +455,10 @@ impl ComposioClient { // from `IntegrationClient`, which we intentionally avoid so the // public surface of that type doesn't widen for one caller. // - // Mirror the TLS settings of the shared client - // (`use_rustls_tls + http1_only`) so this path has the same - // connection behaviour as the other backend calls. - let http_client = reqwest::Client::builder() - .use_rustls_tls() + // Mirror the TLS settings of the shared client so this path has the + // same connection behaviour as the other backend calls. + // Platform-appropriate TLS backend — see [`crate::openhuman::tls`]. + let http_client = crate::openhuman::tls::tls_client_builder() .http1_only() .timeout(std::time::Duration::from_secs(60)) .connect_timeout(std::time::Duration::from_secs(15)) diff --git a/src/openhuman/config/schema/proxy.rs b/src/openhuman/config/schema/proxy.rs index c3f874b6ca..56dc883cdb 100644 --- a/src/openhuman/config/schema/proxy.rs +++ b/src/openhuman/config/schema/proxy.rs @@ -476,9 +476,12 @@ pub fn build_runtime_proxy_client_with_timeouts( service_key, "Failed to build proxied timeout client: {error}" ); - // Apply the same platform TLS selection on the fallback path so the - // error-path client also honors the Windows cert store. - let fb = crate::openhuman::tls::tls_client_builder(); + // Apply the same platform TLS selection and timeouts on the fallback + // path so the error-path client also honors the Windows cert store + // and remains bounded. + let fb = crate::openhuman::tls::tls_client_builder() + .timeout(std::time::Duration::from_secs(timeout_secs)) + .connect_timeout(std::time::Duration::from_secs(connect_timeout_secs)); fb.build().unwrap_or_default() }); set_runtime_proxy_cached_client(cache_key, client.clone());