Original issue tinyhumansai#2359 by @jadrian2006 on 2026-05-20T16:21:59Z
Linux/macOS: OAuth callbacks via openhuman:// don't reach running app instance (Linux equivalent of tinyhumansai#2228 Windows fix)
Summary
This is the Linux/macOS counterpart to tinyhumansai#2228 (Windows desktop OAuth callbacks do not reach the running app instance), which was resolved by PR tinyhumansai#2229 by enabling the deep-link feature on tauri-plugin-single-instance.
That fix alone doesn't help Linux/macOS because the CEF cache-lock preflight at app/src-tauri/src/lib.rs:2074-2078 exits the secondary process with std::process::exit(1) before Tauri's builder runs. The deep-link plugin's forwarding logic lives inside Builder::setup, which is never reached. Result: external openhuman:// callbacks fired by the OS while OpenHuman is already running are dropped, breaking every OAuth flow that returns via deep-link.
Windows works because there is a pre-CEF named-mutex guard at lib.rs:2011-2047 (OPENHUMAN-TAURI-A fix). A second invocation hits ERROR_ALREADY_EXISTS, the secondary's deep-link argv path runs through the plugin's per-platform pre-Builder code, and the URL reaches the primary. Linux/macOS need the equivalent pattern.
Reproduction
- Build from source on Pop!_OS 24.04 + COSMIC Wayland + NVIDIA proprietary driver 580 (any Linux desktop session works; this is not Wayland-specific).
- Tag
v0.54.3-staging. No local patches.
- Launch the shell normally (
cargo tauri dev -- -- --no-sandbox). Shell + embedded core come up clean.
- Start an OAuth flow from inside the shell (e.g. sign-in or "Connect Gmail").
- Complete the authentication in the browser.
- Browser is redirected to
openhuman://auth?token=...&key=auth.
- The OS launches a new OpenHuman binary with that URL as
argv[1].
- The new binary logs
[cef-preflight] CEF cache held by host=<host> pid=<primary pid> and exits 1.
- The primary shell never receives the callback; OAuth state machine remains in its pending state forever.
Diagnostic log (secondary instance, abbreviated)
[startup] platform: arch=x86_64 os=linux
[cef-profile] configured CEF cache user=local path=/home/USER/.openhuman/users/local/cef
[cef-preflight] CEF cache held by host=pop-os pid=1565481 at /home/USER/.openhuman/users/local/cef
[openhuman] CEF cache at /home/USER/.openhuman/users/local/cef is held by another OpenHuman instance (host pop-os, pid 1565481).
Quit the running instance and try again.
Source of the early exit: app/src-tauri/src/lib.rs:2074-2078
#[cfg(any(target_os = "macos", target_os = "linux"))]
if let Err(e) = cef_preflight::check_default_cache() {
eprintln!("\n[openhuman] {e}\n");
std::process::exit(1);
}
Suggested fix
Mirror the Windows pre-CEF guard pattern (lib.rs:2011-2047). On Linux, use a Unix domain socket (or flock(2) over an abstract socket / lockfile) at a stable path like $XDG_RUNTIME_DIR/com.openhuman.app-deeplink.sock. On macOS, use a Mach port or a similar Unix socket. Pseudocode for Linux:
#[cfg(target_os = "linux")]
{
use std::os::unix::net::UnixStream;
let sock_path = std::env::var("XDG_RUNTIME_DIR")
.map(PathBuf::from)
.unwrap_or_else(|_| std::env::temp_dir())
.join(format!("{APP_IDENTIFIER}-deeplink.sock"));
// Collect any openhuman:// URLs from argv
let urls: Vec<String> = std::env::args()
.filter(|a| a.starts_with("openhuman://"))
.collect();
if !urls.is_empty() {
if let Ok(mut stream) = UnixStream::connect(&sock_path) {
// Primary is listening — forward URLs and exit clean.
for url in &urls {
let _ = writeln!(stream, "{url}");
}
log::info!(
"[single-instance] forwarded {} deep-link URL(s) to primary at {}; secondary exiting",
urls.len(),
sock_path.display()
);
std::process::exit(0);
}
// No primary listening — fall through; we'll become the primary.
}
// Primary path: bind the socket and spawn a listener task that calls
// the deep-link plugin's on_event hook for each received URL.
// Listener stays alive for the whole process lifetime.
}
The Tauri plugin's existing on_event handler receives forwarded URLs uniformly via this socket path; the plugin's own D-Bus / argv code stays untouched.
Why the plugin's D-Bus path doesn't help
tauri-plugin-deep-link on Linux registers a D-Bus name in its plugin setup hook, which runs inside Builder::build() — after CEF init. The Builder is never constructed on a secondary instance because cef_preflight::check_default_cache() returns Err and std::process::exit(1) fires first. The plugin's D-Bus forwarding code therefore can't claim the role of "secondary forwarder" in this scenario.
Environment
- OpenHuman: built from source, tag
v0.54.3-staging (commit ebd6457)
- OS: Pop!_OS 24.04 LTS (Ubuntu noble base)
- Kernel: 6.18.7-76061807-generic
- Session: Wayland (COSMIC compositor)
- GPU: NVIDIA RTX 4070 Ti, driver 580.159.03
- GLIBC: 2.39
- Reproducible on every OAuth attempt; the only "OAuth that worked" cases were ones where the user had no OpenHuman running, so the deep-link callback became the new shell with the URL in argv and the OAuth state happened to be re-derivable.
Related
Happy to test patches.
Linux/macOS: OAuth callbacks via openhuman:// don't reach running app instance (Linux equivalent of tinyhumansai#2228 Windows fix)
Summary
This is the Linux/macOS counterpart to tinyhumansai#2228 (Windows desktop OAuth callbacks do not reach the running app instance), which was resolved by PR tinyhumansai#2229 by enabling the
deep-linkfeature ontauri-plugin-single-instance.That fix alone doesn't help Linux/macOS because the CEF cache-lock preflight at
app/src-tauri/src/lib.rs:2074-2078exits the secondary process withstd::process::exit(1)before Tauri's builder runs. The deep-link plugin's forwarding logic lives insideBuilder::setup, which is never reached. Result: externalopenhuman://callbacks fired by the OS while OpenHuman is already running are dropped, breaking every OAuth flow that returns via deep-link.Windows works because there is a pre-CEF named-mutex guard at
lib.rs:2011-2047(OPENHUMAN-TAURI-Afix). A second invocation hitsERROR_ALREADY_EXISTS, the secondary's deep-link argv path runs through the plugin's per-platform pre-Builder code, and the URL reaches the primary. Linux/macOS need the equivalent pattern.Reproduction
v0.54.3-staging. No local patches.cargo tauri dev -- -- --no-sandbox). Shell + embedded core come up clean.openhuman://auth?token=...&key=auth.argv[1].[cef-preflight] CEF cache held by host=<host> pid=<primary pid>and exits 1.Diagnostic log (secondary instance, abbreviated)
Source of the early exit:
app/src-tauri/src/lib.rs:2074-2078Suggested fix
Mirror the Windows pre-CEF guard pattern (lib.rs:2011-2047). On Linux, use a Unix domain socket (or
flock(2)over an abstract socket / lockfile) at a stable path like$XDG_RUNTIME_DIR/com.openhuman.app-deeplink.sock. On macOS, use a Mach port or a similar Unix socket. Pseudocode for Linux:The Tauri plugin's existing
on_eventhandler receives forwarded URLs uniformly via this socket path; the plugin's own D-Bus / argv code stays untouched.Why the plugin's D-Bus path doesn't help
tauri-plugin-deep-linkon Linux registers a D-Bus name in its pluginsetuphook, which runs insideBuilder::build()— after CEF init. The Builder is never constructed on a secondary instance becausecef_preflight::check_default_cache()returnsErrandstd::process::exit(1)fires first. The plugin's D-Bus forwarding code therefore can't claim the role of "secondary forwarder" in this scenario.Environment
v0.54.3-staging(commit ebd6457)Related
tauri-plugin-single-instancedeep-link feature)cef::initializepanic before preflight existed; the cef_preflight code added since handles that case correctly but doesn't address deep-link forwardingHappy to test patches.