Skip to content

fix(config): a saved password that failed to load connected silently with a blank one (FR-CFG-08) - #202

Merged
dc0sk merged 1 commit into
mainfrom
fix/silent-empty-saved-password
Sep 22, 2026
Merged

dc0sk merged 1 commit into
mainfrom
fix/silent-empty-saved-password

Conversation

@dc0sk

@dc0sk dc0sk commented Sep 22, 2026

Copy link
Copy Markdown
Owner

What

A saved connection password that fails to load from the OS keychain — a locked
or unreachable keyring, a timed-out read — was silently treated the same as
"no password was ever saved". The app then tried to connect with a blank
password, which the K4 rejects, producing an unexplained connect-and-drop
within about 1.5 s and zero read-back (not even VFO frequency), with nothing
in the UI to say why.

How found

DC0SK migrated the host OS (Manjaro → AerynOS) and hit this on the live K4.
Bisection ruled out a code regression: every build back to and including the
last release (v0.9.0, unmodified) reproduced it, so the actual cause is
environmental (most likely the login keyring not being unlocked/populated on
the new profile) — confirmed by typing the password in manually, which
connects cleanly. But the underlying defect this PR fixes is real and
independent of that: any keychain read failure, on any machine, produces
this same silent, unexplained symptom.

The fix

  • SecretStore::get now returns Result<Option<String>, SecretError> instead
    of Option<String>. Ok(None) means "no secret for this account" (not an
    error); Err means the store itself could not be read. The two were
    previously indistinguishable.
  • Both places that read a saved password (App::new at startup, select_peer
    when choosing a keychain-backed cached peer) used to collapse any read
    failure with .ok().flatten().unwrap_or_default(). They now go through
    load_secret_timed/password_load_outcome, which keep "no password saved",
    "the store failed", and "the read timed out" as three distinct outcomes.
  • The connection panel shows a warning next to the password field (in the
    existing Caution colour) whenever the load did not cleanly come back as
    "found" or "nothing saved" — visible before you click Connect, instead of
    manifesting only as an unexplained failure afterward.

Tests

  • fr_cfg_08_no_password_saved_is_not_a_warning,
    ..._a_saved_password_loads_with_no_warning,
    ..._a_store_failure_warns_and_names_the_reason,
    ..._a_timeout_warns_distinctly_from_a_failure_or_a_miss — the pure decision
    function, all four outcomes.
  • ..._wiring_a_failing_store_reads_as_an_error_not_a_miss,
    ..._wiring_a_hung_store_times_out_instead_of_blocking — through the real
    thread/channel wiring (a hung store is abandoned at the bound, not blocked
    on).
  • fr_cfg_08_a_store_failure_is_not_a_clean_miss (k4-config).
  • Sabotage: reverted the decision function to the original bug (collapse
    every non-clean-hit outcome to (empty, no warning)) — 2 of 6 tests failed,
    confirming they catch the actual regression.

Verified

  • cargo fmt --all -- --check, cargo clippy --workspace --all-targets --all-features, cargo clippy -p k4remote --features kpod --all-targets
    all clean.
  • cargo test --workspace — all green.
  • cargo xtask — 0 R3 gaps, FR-CFG-08 traced.

Not covered / still yours

  • The actual root cause on your machine (keyring state after the OS
    migration) — separate from this fix, and outside what a rebuild can
    change. Worth checking secret-tool search service k4remote or
    equivalent, and that the login keyring auto-unlocks with your session.
  • See the warning appear on screen against a real locked/unreachable
    keychain (e.g. temporarily stop gnome-keyring-daemon and restart with
    a saved peer selected).

…with a blank one (FR-CFG-08)

Found on DC0SK's live K4 after a host OS migration: the app connected for
~1.5s with zero read-back and disconnected, reproducing on every build back
to and including the last release, ruling out a code regression here. Root
cause: SecretStore::get returned Option<String>, so "no entry" and "the
store itself failed" (locked keyring, D-Bus unreachable, a timed-out read)
were the same value, and both call sites collapsed a failed read to an empty
password with .ok().flatten().unwrap_or_default() and no warning.

SecretStore::get now returns Result<Option<String>, SecretError>, threaded
through a new load_secret_timed/password_load_outcome pair, with a warning
shown next to the password field whenever a load did not cleanly come back
as "found" or "nothing saved".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@dc0sk
dc0sk force-pushed the fix/silent-empty-saved-password branch from ed057f4 to 11eee4f Compare September 22, 2026 18:13
@dc0sk
dc0sk merged commit 1f88de3 into main Sep 22, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant