The token encryption key mixed in the XDG portal secret (session-bus
dependent): a login run in a graphical session wrote a token an
init-started/headless qbzd could never decrypt, reported as a bare
"not logged in". PortalKey policy: desktop root keeps Session, daemon
root uses Never; one-shot migration on read (decrypt with Session,
rewrite portal-free); oauth_token_file_present_at() +
latch_undecryptable_token() so status distinguishes "no token" from
"token I cannot decrypt". Live-verified 3x (OpenRC, headless Pi,
migration path).
km
zbus features unify across the dependency graph and "tokio" wins over
"async-io": enabling it via rfd, ashpd (secret portal) and ksni's
default features flipped the shared zbus 5 executor to tokio for
accesskit_unix/atspi too — Slint's accessibility backend opens its
D-Bus connection on a plain thread and panicked at startup ("there is
no reactor running, must be called from the context of a Tokio 1.x
runtime"), killing AT-SPI support. Switch every zbus-5 consumer we
control to the async-io backend (rfd "async-std", ashpd "async-io",
ksni default-features off + "async-io"): async-io connections
self-drive from any thread and the futures still await fine inside our
tokio runtime. The zbus 4 group (keyring/secret-service/mpris-server/
qbz-audio) is a separate feature graph and stays on tokio by design.
cc
Add /track/lyricsUrl (signed RPC, method name "tracklyricsUrl") plus
the CDN document fetch through the offline choke point. Wire shapes
were validated against live captures (2026-06-10) and diverge from the
inferred yaml: numeric track_id in the step-1 envelope, CDN document
wrapped in {translation_langs, publishers, writers, original}, "wsync"
word-synced discriminator instead of "lsync" (per-word timestamps),
plain lines as objects, and miss = HTTP 404 error body mapped to a
typed Ok(None) instead of an error. Unit tests deserialize fixture
excerpts of the captures; examples/lyrics_probe.rs is the read-only
live probe used for the capture.
Also expose qbz_credentials::load_oauth_token_from_file so a stale
keyring entry cannot shadow the authoritative encrypted token file.
cc
Moves the credentials module (keyring + AES-256-GCM encrypted file
store for Qobuz credentials and the OAuth token) out of src-tauri into
a new qbz-credentials crate. src-tauri/credentials re-exports it, so the
Tauri app keeps identical behavior while the Slint shell can share the
exact same implementation, encrypted file, and keyring entries.
The module was already fully self-contained (no Tauri, no crate::
references); only the dependency declarations move with it.
cc