tauri-apps / tauri-apps/plugins-workspace
single-instance: fail-closed ownership and pre-IPC secondary activation on Windows, Linux, and macOS
- Dominant language
- Rust
- Stars
- 1.8k
- Forks
- 602
- Avg merge
- 4d 14h
- Merged PRs (30d)
- 9
Description
## Summary
`tauri-plugin-single-instance` can admit a second unguarded process (or drop a
secondary activation) when ownership / IPC setup races or non-primary errors
occur. This issue proposes aligning desktop backends on **fail-closed ownership**
and **race-safe secondary notification**, without changing app-specific callback
policy.
Related: #3495 (Windows mutex → HWND race; open; timeout path currently
fail-open; maintainer feedback prefers simpler exit-if-mutex-held).
## Expected invariant
1. At most one process of a given app identifier becomes the owning primary.
2. If ownership cannot be established or queried safely, setup **fails** (error
propagation / exit) instead of continuing as a normal second main.
3. A secondary that arrives after the ownership token exists but **before** the
IPC receiver is ready must still deliver argv/cwd to the primary within a
**bounded** wait, then exit — or fail closed if delivery cannot complete.
4. Linux: treat D-Bus errors other than `NameTaken` as ownership failure, not
as “continue as primary”.
5. macOS: separate exclusive ownership from IPC (socket), with explicit failure
when ownership cannot be taken; avoid “launch normally” on lock/bind errors.
6. Public API stays generic: callback decides focus / deep-link / silence;
no hardcoded product CLI flags.
## Current behavior (2.4.3)
### Windows
`CreateMutexW` runs before the IPC HWND exists. If
`GetLastError() == ERROR_ALREADY_EXISTS` and `FindWindowW` is null, setup
returns `Ok(())` and the process continues. Under load this admits a second
main and can leak the mutex handle so later launches also slip through.
(Described in detail in #3495.)
### Linux
On `zbus::Error::NameTaken`, the secondary notifies and exits. Other connection
/ name / serve errors take the `_ => {}` arm and the process continues. Builder
setup also uses `unwrap` on several steps.
### macOS
The Unix socket is both discovery and (effectively) ownership. Several error
paths log and **launch normally** (notify failures other than
NotFound/ConnectionRefused; listener bind failure). 2.4.3 switched the listener
to `tokio::net::UnixListener` (#3466) but did not change fail-open semantics.
## Proposed direction (for discussion)
- Windows: close the mutex→HWND gap with bounded wait **and** fail-closed
timeout (or another design that preserves mid-gap activation without
dual-owner). Clarify relationship to #3495.
- Linux: map non-`NameTaken` init failures to setup `Err` / exit; avoid silent
fall-through; reduce panic-prone `unwrap` on ownership path.
- macOS: exclusive ownership (file lock or equivalent) + socket for IPC only;
fail closed when ownership cannot be acquired. If using `std::fs::File::try_lock`,
note MSRV: API is stable only since Rust **1.89**, while this crate declares
**1.77.2** — either keep an MSRV-compatible lock or explicitly propose raising
MSRV.
- Add minimal reproducers / tests where feasible.
- Keep optional deep-link / semver features unchanged unless required.
## Compatibility
- Intended as a behavior hardening for apps that assume true single-instance.
- Apps that relied on fail-open “launch a second main when IPC is broken” would
see stricter failures (preferable for most desktop apps; call out in changelog).
- No requirement to change the public callback signature.
## Environment
- Crate: tauri-plugin-single-instance 2.4.3
- Repo: tauri-apps/plugins-workspace (`plugins/single-instance`)
- Platforms: Windows, Linux (session bus), macOS
## Ask
Please confirm preferred design for:
1. Windows mid-gap activation vs “exit immediately if mutex exists” (#3495),
2. fail-closed timeout policy,
3. macOS lock API vs MSRV 1.77.2,
before we open platform-scoped PRs with `.changes` entries and signed commits.
Contributor guide
Research direction
Start in plugins/single-instance and trace the Windows mutex-to-HWND path, Linux ownership setup, and macOS socket ownership and notification paths described in the issue. Confirm the maintainer’s choices for the Windows race, fail-closed timeout, and macOS lock/MSRV approach before defining platform-scoped tests and reproducers. Done means ownership failures cannot continue as a second main and secondary activation is delivered or fails closed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- desktop
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Needs clarification
- Newbie friendliness
- 28/100