From a17f70c4ac0fb88a280db93500cb35ddd8aba4b3 Mon Sep 17 00:00:00 2001 From: michael Date: Mon, 27 Apr 2026 09:05:58 -0700 Subject: [PATCH] docs(plan): fix Phase 8 zbus runtime path (no glib feature exists) zbus 4.4 does not expose a glib feature; my earlier draft was wrong about that. Switch the plan to use the workspace tokio config and install a multi-thread tokio runtime in main.rs, with the runtime guard kept alive for the lifetime of app.run(). glib::MainContext::spawn_local still drives the widget-touching closures; tokio's reactor wakes them. Also: hoist futures-util into the Task 1 dep bundle so Task 6 doesn't need a separate Cargo.toml edit. --- docs/plans/2026-04-27-phase-8-gui-keys.md | 41 ++++++++++++++--------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/docs/plans/2026-04-27-phase-8-gui-keys.md b/docs/plans/2026-04-27-phase-8-gui-keys.md index b3a2b99..a122673 100644 --- a/docs/plans/2026-04-27-phase-8-gui-keys.md +++ b/docs/plans/2026-04-27-phase-8-gui-keys.md @@ -4,12 +4,15 @@ **Goal:** Land a working `authforge` GUI that lists the current user's enrolled FIDO2 credentials, enrolls a new key with a touch-prompt modal driven by the daemon's `EnrollmentSucceeded`/`EnrollmentFailed` signals, removes a credential, and degrades gracefully when the daemon is unreachable. -**Architecture:** Single-window GTK4 + libadwaita app on the user's session, talking to the daemon on the **system bus** at `io.dangerousthings.AuthForge / /io/dangerousthings/AuthForge / io.dangerousthings.AuthForge1`. zbus uses its **glib** integration (`features = ["glib"]`) instead of the workspace tokio config, so all async runs on the GTK main thread via `glib::MainContext::spawn_local`. No tokio dep in the GUI process. +**Architecture:** Single-window GTK4 + libadwaita app on the user's session, talking to the daemon on the **system bus** at `io.dangerousthings.AuthForge / /io/dangerousthings/AuthForge / io.dangerousthings.AuthForge1`. zbus uses the workspace tokio runtime; a multi-thread tokio runtime is installed at app startup and its enter-guard is held for the lifetime of `app.run()`. Closures that touch widgets dispatch async work via `glib::MainContext::spawn_local`, which polls the future on the GTK main thread; tokio's reactor (running on its own worker threads) wakes the future when zbus I/O is ready, and glib re-polls. + +> **Plan note (2026-04-27):** an earlier draft of this plan claimed zbus 4 has a `glib` feature for runtime integration. It does not — zbus 4.4's features are `async-io` (default), `tokio`, `bus-impl`, `chrono`, `p2p`, `time`, `url`, `uuid`, `vsock`. We use `tokio` to match the rest of the workspace. **Tech Stack:** - `gtk4-rs` 0.8 (already pinned in `gui/Cargo.toml`). - `libadwaita` 0.6 (already pinned). -- `zbus = { version = "4", default-features = false, features = ["glib"] }` — **not** `workspace = true`; the workspace dep is tokio-flavored and would conflict. +- `zbus = { workspace = true }` — workspace config is `default-features = false, features = ["tokio", "p2p"]`. +- `tokio = { workspace = true }` — needed for the runtime guard that hosts zbus's tokio executor. - `authforge-common = { path = "../common" }` for the shared `Credential` wire type. **Reference:** @@ -38,18 +41,20 @@ **Step 1:** Append to `gui/Cargo.toml` `[dependencies]`: ```toml -zbus = { version = "4", default-features = false, features = ["glib"] } +zbus = { workspace = true } +tokio = { workspace = true } +futures-util = { workspace = true } ``` -(Do **not** use `workspace = true`. The workspace zbus is tokio-flavored — the GUI needs the glib runtime feature, which is mutually exclusive at the zbus crate level.) +The tokio runtime guard installed in Task 4 (`main.rs`) is what makes zbus's tokio-flavored futures actually run when `glib::MainContext::spawn_local` polls them. `futures-util` shows up here so Task 6's `select!`-style race in `enroll_dialog.rs` doesn't need a follow-up Cargo.toml edit. -**Step 2:** `cargo build -p authforge-gui`. Expected: clean compile (no usages yet, but the dep resolves). +**Step 2:** `cargo build -p authforge-gui`. Expected: clean compile (no usages yet, but the deps resolve). **Step 3:** Commit. ```bash git add gui/Cargo.toml Cargo.lock -git commit --no-gpg-sign -m "chore(gui): add zbus glib feature for D-Bus client" +git commit --no-gpg-sign -m "chore(gui): add zbus + tokio + futures-util deps for D-Bus client" ``` --- @@ -357,6 +362,16 @@ mod keys_page; const APP_ID: &str = "io.dangerousthings.AuthForge"; fn main() -> glib::ExitCode { + // Multi-thread tokio runtime hosts zbus's tokio-flavored futures. The + // `_guard` keeps the runtime context active on this thread so calls like + // `zbus::Connection::system().await` from within `glib::MainContext:: + // spawn_local` closures don't panic with "no current reactor". + let runtime = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .expect("build tokio runtime"); + let _guard = runtime.enter(); + let app = adw::Application::builder().application_id(APP_ID).build(); app.connect_activate(|app| { let win = adw::ApplicationWindow::builder() @@ -624,13 +639,7 @@ fn show_failure(dialog: &adw::AlertDialog, msg: &str) { } ``` -**Step 3:** Add `futures-util` to `gui/Cargo.toml`: - -```toml -futures-util = { workspace = true } -``` - -**Step 4:** Replace the `start_enroll` body in `gui/src/keys_page.rs`: +**Step 3:** Replace the `start_enroll` body in `gui/src/keys_page.rs`: ```rust async fn start_enroll(self: &Rc) { @@ -648,12 +657,12 @@ futures-util = { workspace = true } } ``` -**Step 5:** `cargo build -p authforge-gui && cargo clippy -p authforge-gui --all-targets -- -D warnings`. Clean. +**Step 4:** `cargo build -p authforge-gui && cargo clippy -p authforge-gui --all-targets -- -D warnings`. Clean. -**Step 6:** Commit. +**Step 5:** Commit. ```bash -git add gui/src/enroll_dialog.rs gui/src/keys_page.rs gui/src/main.rs gui/Cargo.toml Cargo.lock +git add gui/src/enroll_dialog.rs gui/src/keys_page.rs gui/src/main.rs git commit --no-gpg-sign -m "feat(gui): TouchDialog modal racing EnrollOwn call against EnrollmentFailed signal" ```