Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/linux-credential-worker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Linux credential worker foundation

Issue #722, dependent on #721. This component is not started by the unavailable Linux runtime. It does not activate Bluetooth, approve pairing, inject input or change existing synchronous model startup. Production ownership and startup migration remain separate work.

One worker owns one dedicated blocking thread and admits at most one outstanding load, save or delete. Overlapping callers receive `Busy`; there is no retry queue or per-request thread spawning. A caller-supplied timeout must be greater than zero and at most 30 seconds. Device identifiers are limited to 128 bytes and tokens to 4096 bytes; empty identifiers/tokens are rejected. Backend errors become the fixed `StorageUnavailable` result, with no raw native error or credential logging.

Timeout and dropping a future invalidate its result. Generation invalidation discards stale results; queued work checks cancellation before calling the backend. Cancellation can race with starting a native call and cannot interrupt it or roll back a save/delete already underway. A timed-out write may still commit. Callers must never publish/approve a credential from a cancelled or failed operation and must reconcile persisted state before retrying a replacement.

Admission remains busy until the native call returns, even after caller timeout. Shutdown stops admission, invalidates results and closes the channel without joining the thread. A stuck native call may therefore leave a detached thread until process exit. The future runtime must own exactly one worker and must not recreate workers on timeout; otherwise repeated recreation would defeat the thread bound. Restart is the recovery path for a permanently stuck worker.

Fake-store tests cover save/load/delete, request and response bounds, sanitized errors, timeout while a call remains blocked, busy admission, discarded stale results, recovery, dropped futures and non-blocking shutdown. They perform no real keyring operations. Physical Secret Service qualification and subscriber isolation are still required before enabling secure Linux pairing; see [credential storage gates](linux-credentials.md).
21 changes: 21 additions & 0 deletions docs/linux-credentials.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Linux credential storage foundation

Issue #720. This is storage hardening on `linux-support`, not an enabled secure Linux runtime.

The pinned keyring 4.1.6 default `v1` adapter selects Secret Service on Linux, Windows Credential Manager on Windows, and Keychain Services on macOS. Linux continues to use the existing service `com.enaboapps.switchify.pc.pairing` and device-ID lookup keys. No plaintext, kernel-keyring or memory fallback is added. Dependencies, state schema and other platforms' storage selection are unchanged.

The Linux wrapper serializes operations on each storage instance. Save succeeds only when a nonempty token is written and read back unchanged. Missing credentials remain distinct from an inaccessible store; empty stored values fail closed. Backend errors are replaced with a fixed remediation message, never raw D-Bus text, credential attributes or tokens. Unlock the desktop Secret Service keyring; ensure a Secret Service provider is installed/running, then restart Switchify PC. The pinned keyring adapter caches initial store initialization, so merely retrying after an initial service failure may not recover in the same process.

A failed verification does not delete the credential: a write may have succeeded before a read failed, and destructive rollback could erase existing access. A replacement write can therefore change the backend even when verification reports failure; it is not a transaction or a durability guarantee. Future pairing integration must not approve/publish a token on any storage error and must account for replacement failure recovery.

The existing model restores pairing tokens transactionally into the protocol engine. If any load fails, no tokens are activated, and saved pairing metadata is preserved when settings are persisted. Restarting after storage recovery can restore access. Ordinary missing entries retain existing behavior; this wrapper does not infer that every missing entry means the whole store failed.

## Validation and remaining gates

Fake-store tests cover successful write verification, recreated wrapper reads, empty credentials, mismatched read-back, save/load/delete failures, non-destructive verification failure and model metadata preservation/recovery. They never access a real keyring or Bluetooth device. Recreating a fake adapter is not physical persistence evidence.

Before enabling Linux pairing, manually qualify an unlocked store, locked/missing provider, permission denial, application restart, logout/login, failed replacement and explicit forgetting on each supported desktop. No new readiness probe writes test credentials or prompts at startup. Bounded/off-main-thread credential operations and recovery UX belong to production runtime integration; this wrapper retains the synchronous storage API.

Subscriber isolation remains a separate unresolved gate. This change cannot approve Linux pairing or inject input.

Reference: [keyring 4.1.6 v1 adapter](https://docs.rs/keyring/4.1.6/keyring/v1/index.html).
3 changes: 3 additions & 0 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,8 @@ windows = { version = "0.62.2", features = [
] }
winreg = "0.55"

[dev-dependencies]
tokio = { version = "1", features = ["test-util"] }

[patch.crates-io]
corebluetooth-rs = { path = "../vendor/corebluetooth-rs" }
4 changes: 4 additions & 0 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ mod grid3;
#[cfg_attr(target_os = "linux", allow(dead_code))]
mod input;
#[cfg(target_os = "linux")]
// Prepared for runtime integration; not started by the unavailable Linux runtime.
#[allow(dead_code)]
mod linux_credential_worker;
#[cfg(target_os = "linux")]
mod linux_runtime;
#[cfg(target_os = "macos")]
mod macos;
Expand Down
Loading