diff --git a/README.md b/README.md index c4b61c4..6d8b791 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,12 @@ See [docs/BUILDING.md](docs/BUILDING.md). ## Status -Pre-alpha. Phases 0–7, 15, 16 code complete (workspace scaffold, D-Bus daemon, +Pre-alpha. Phases 0–8, 15, 16 code complete (workspace scaffold, D-Bus daemon, storage layer, FIDO2 backend, policy apply via pam-auth-update with lockout -simulation, PAM pending-flag backstop, `authforgectl` CLI, `gnome-control-center` -overlay, Ansible role for fleet deployment). Phase 8 (GUI Security Keys tab) and -Phase 12 (recovery flow) are the next single-lane bundles. See [docs/plans/](docs/plans/) -for design, roadmap, and per-phase implementation plans. +simulation, PAM pending-flag backstop, `authforgectl` CLI, GUI Security Keys +tab, `gnome-control-center` overlay, Ansible role for fleet deployment). +Phase 9 (GUI Policy tab) and Phase 12 (recovery flow) are the next single-lane +bundles. See [docs/plans/](docs/plans/) for design, roadmap, and per-phase +implementation plans. Copyright 2026 Dangerous Things, LLC. Licensed under the Apache License, Version 2.0. diff --git a/docs/plans/2026-04-26-authforge-implementation.md b/docs/plans/2026-04-26-authforge-implementation.md index fd0719f..ec49815 100644 --- a/docs/plans/2026-04-26-authforge-implementation.md +++ b/docs/plans/2026-04-26-authforge-implementation.md @@ -35,9 +35,9 @@ | 5 | Daemon: lockout simulator | 3 days | ✅ **Done** — **Bundled** with Phase 4 | | 6 | PAM module: `pam_authforge_pending.so` (C) | 3 days | ✅ **Code complete** (2026-04-27) — `pamtester` smoke recipe in `pam/TESTING.md` | | 7 | CLI: `authforgectl` | 4 days | ✅ **Done** (2026-04-27) — 14 subcommands | -| 8 | GUI: app shell + Security Keys tab | 6–8 days | **Spec'd** — next single-lane bundle (see § Parallel Execution Lanes) | +| 8 | GUI: app shell + Security Keys tab | 6–8 days | ✅ **Code complete** (2026-04-27) — 4 unit tests; manual smoke recipe in `gui/TESTING.md` | | 9 | GUI: Policy tab + lockout-warning UX | 4 days | **Spec'd** — opens after Phase 8 | -| 10 | First-login flow: autostart entry + fullscreen modal | 4 days | **Spec'd** — needs Phase 6 ✓ + Phase 8 | +| 10 | First-login flow: autostart entry + fullscreen modal | 4 days | **Spec'd** — needs Phase 6 ✓ + Phase 8 ✓ | | 11 | TOTP support (PAM module + GUI tab) — feature flag | 5 days | **Spec'd** — opens now (needs Phase 2 ✓ + Phase 4 ✓) | | 12 | Recovery flow (codes + emergency unlock) | 4 days | **Spec'd** — opens now (needs Phase 6 ✓) | | 13 | Debian packaging finalization (postinst, debconf, purge) | 4 days | **Spec'd** — sequential tail; needs all binaries built | @@ -48,7 +48,7 @@ | 18 | User docs + onboarding site | 3 days | **Spec'd** — anytime slot | | **R** | **v1.0 release** | 1 day | — | -**Progress: 11 of 19 phases code-complete (58%).** Total original estimate was ~14 weeks of focused work for v1.0. Subsequent phases (Debian packaging, KDE port, RPM) are scoped in the design doc. +**Progress: 12 of 19 phases code-complete (63%).** Total original estimate was ~14 weeks of focused work for v1.0. Subsequent phases (Debian packaging, KDE port, RPM) are scoped in the design doc. --- @@ -73,9 +73,9 @@ The phase numbering is a dependency-respecting ordering, **not** a serial schedu └──────┬───┘ │ │ ↓ │ │ ┌───────────┐ │ │ - Phase 8 Phase 9 Phase 12 Phase 16 ✅ + Phase 8 ✅ Phase 9 Phase 12 Phase 16 ✅ GUI keys GUI policy recovery Ansible (parallel agent) - (next) (after 5) (open) + (open) (open) │ │ │ └──────────┼────────────┘ ↓ @@ -105,13 +105,13 @@ Three lanes touch disjoint file trees and can land in any order: | Lane | Phases | Touches | Why parallel-safe | |---|---|---|---| -| **GUI** | 8 (then 9, after 5 ✓) | `gui/src/` | The D-Bus contract is fully wired; GUI is a pure consumer. Needs `libgtk-4-dev` + `libadwaita-1-dev` on the box. | +| **GUI** | 9 (after 8 ✓) | `gui/src/views/policy.rs` (new), small additions to `keys_page.rs` for the policy-violation banner | Phase 8 (Keys tab) landed 2026-04-27; Phase 9 (Policy tab) reuses the same shell + ToastOverlay + Daemon client. | | **Recovery** | 12 | `daemon/src/recovery.rs`, `pam/pam_authforge_pending.c` (recovery hook), small `dbus.rs` `GenerateRecoveryCode` body | The PAM module already has a `recovery_code_matches` stub returning 0; Phase 12 makes it real. Daemon side is brand-new file. | -| **TOTP** | 11 | `daemon/src/totp/`, `gui/src/views/totp.rs` (when 8 lands), `debian/control` Recommends | Feature-flagged at build time; only the daemon-side bits open right now. GUI tab waits on Phase 8. | +| **TOTP** | 11 | `daemon/src/totp/`, `gui/src/views/totp.rs`, `debian/control` Recommends | Feature-flagged at build time. Phase 8 ✓ unblocked the GUI tab; both daemon + GUI surfaces open now. | ### Sequential tail (no parallelism wins) -Phase 10 (first-login) needs both Phase 6 ✓ and Phase 8. Phase 13 (deb finalization) needs every binary built. Phase 14 (PPA) chains off 13 — and is the **VM smoke gate** for everything currently in "Code complete" status. Phase 17 (integration tests) chains off 14. Phase 18 (user docs) and R (release) follow. +Phase 10 (first-login) needs both Phase 6 ✓ and Phase 8 ✓ — now fully unblocked. Phase 13 (deb finalization) needs every binary built. Phase 14 (PPA) chains off 13 — and is the **VM smoke gate** for everything currently in "Code complete" status. Phase 17 (integration tests) chains off 14. Phase 18 (user docs) and R (release) follow. ### Lessons learned from this session's parallelism @@ -230,6 +230,28 @@ Both delivered by parallel subagents in `git worktree` isolation while Lane 1 (P - All 5 YAML files validated via `yaml.safe_load`. `ansible-lint` skipped — not installed in the harness sandbox. - **Subagent-initiated divergences (kept):** added `authforge_ppa` override variable so internal-mirror users don't fork the role; pre-creates `/etc/authforge/policy.d/` defensively (idempotent); standard `ansible_managed` template header. +### Phase 8 closeout notes (2026-04-27) + +Single-lane execution in a worktree at `.claude/worktrees/phase-8-gui-keys`. Plan at [2026-04-27-phase-8-gui-keys.md](2026-04-27-phase-8-gui-keys.md). Eight task commits + one plan correction + merge commit (`943d9b0`). + +**What landed:** +- `gui/src/error.rs` — `classify(&zbus::Error)` + `user_message()` mapping zbus errors to `Disconnected` / `AccessDenied` / `Other`. 4 unit tests, all green. +- `gui/src/bus.rs` — `Daemon` wrapper (Clone via inner `Arc`) with `list_credentials`, `enroll_own`, `remove_own`, `subscribe_enrollment_failed`, plus `current_user()` helper. Mirrors [cli/src/bus.rs](../../cli/src/bus.rs) pattern. +- `gui/src/keys_page.rs` — `KeysPage` widget. Empty state with retry banner when daemon unreachable, otherwise lists each credential as an `adw::ActionRow` with a per-row trash button + "Enroll a new key" activatable row at the bottom. Refresh re-runs `ListCredentials` from scratch. +- `gui/src/enroll_dialog.rs` — modal `adw::AlertDialog` with spinner. Spawns one task that races `enroll_own` against the `EnrollmentFailed` signal stream via `futures_util::future::select`; success comes from the call's return (`Credential`), the signal lets a fast failure get a useful message before the call's typed `Err` traverses the bus. +- `gui/src/main.rs` — multi-thread tokio runtime + `runtime.enter()` guard held for `app.run()`. `adw::ApplicationWindow` containing `gtk::Box[HeaderBar, ViewStack]` wrapped in `adw::ToastOverlay`. `KeysPage` is the only visible page; the `ViewStack` is forward-looking for Phase 9. +- `gui/TESTING.md` — automated gate (`cargo build`, `clippy`, `test`) + manual smoke recipe for a Yubikey-equipped Ubuntu box. + +**Plan deviations (recorded in commit messages):** +- The plan asserted `zbus = { features = ["glib"] }` for runtime integration — zbus 4.4 has no such feature. Switched to the workspace tokio config + a tokio runtime guard in `main`. Plan correction commit (`a17f70c`) ships alongside the implementation. +- `adw::ToolbarView` is libadwaita `v1_4`-gated; the project sticks with `gtk::Box` vertical layout (commit `c6a5e94` established this). +- `adw::AlertDialog` is libadwaita `v1_5`-gated; `gui/Cargo.toml` enables that feature. Targets Ubuntu 24.04+ (libadwaita 1.5). +- `Proxy::new(...).into_owned()` doesn't exist in zbus 4.4; used `Proxy::new_owned(...)` (the CLI's known-working pattern). +- Dropped the planned `subscribe_enrollment_succeeded` from `bus.rs` — the dialog only races against the failed signal because the call's return value carries the new `Credential` (source of truth for success). YAGNI. + +**Deferred to Phase 14 PPA-build VM smoke:** +- All six steps in `gui/TESTING.md` (touch a real Yubikey through the modal; remove a credential; pull the key during enrollment to see the failure swap; kill the daemon to see the disconnected banner). + --- # Phase 0: Repository Scaffolding (FULLY DETAILED)