# Handoff ## Project state A Common Lisp GTK4/WebKitGTK web-app runner is implemented in this repository. - Run from checkout: `./bin/web-app-runner ` - Build standalone executable: `./bin/build-web-app-runner` → `./web-app-runner` - Test: `asdf:test-system :clgtk4-web-app-runner/tests` (command in `README.md`) - ASDF system: `clgtk4-web-app-runner` - Main source: `src/web-app-runner.lisp` - Main branch now contains the merged pass credential feature and the follow-up privacy/credentials UX changes. - The uncommitted Tomb-backed profile work remains stashed on the `feature/tomb-backed-profiles` branch as `WIP Tomb-backed profiles before main merge`. - The previous main tip was `ff33b18`; the pass branch was merged with a merge commit before the current uncommitted changes. The generated executable contains SBCL and the Lisp application; it does not need Quicklisp or the checkout at runtime, but still dynamically needs GTK4, WebKitGTK, and their typelibs. ## Terminology - **Runner**: one `web-app-runner` process and its PIN-gated browsing session. - **Runner window**: one GTK browser window owned by that process. - **Runner profile**: target-specific WebKit data/cache directory, not a profile shared with other browsers or WebKit applications. ## Implemented behavior ### PIN and data permissions - First launch sets a six-digit PIN; later launches require it before a WebKit session or WebView is created. - The verifier is a PBKDF2-SHA256 verifier (600,000 iterations) at `$XDG_DATA_HOME/web-app-runner/pin-verifier` (fallback: `~/.local/share/web-app-runner/pin-verifier`). The PIN itself is never stored. If it is missing while runner profiles exist, startup deletes all runner profile data/cache before offering replacement-PIN setup, preventing a new PIN from silently unlocking retained authenticated sessions. - Five unsuccessful PIN entries impose a 30-second cooldown. - Runner data/cache/profile directories are explicitly chmodded to `0700`. The PIN verifier is chmodded to `0600`; existing verifier permissions are corrected when it is read. - This is ordinary same-account filesystem protection, not encryption or a defense against a process/user that can already use the account. - PBKDF2 verification measured about 1.33 seconds and allocated heavily in an earlier measurement. Do not weaken it without deciding the local-PIN threat model. ### Isolated profiles and cleanup - A persistent WebKit network session is created only after PIN success and after a target URL is known. - Profiles live under `$XDG_DATA_HOME/web-app-runner/profiles//` and cache under `$XDG_CACHE_HOME/web-app-runner/profiles//`. - The profile includes cookies, local storage, IndexedDB, cache, service-worker data, etc., and is separate from other WebKitGTK apps and other target keys. - `Clear runner data on close` is synchronized across a runner's windows. It changes no live data when toggled. After the final window closes, WebKitGTK clears the target profile asynchronously and the runner waits up to 15s. - Cleanup errors or timeouts retain the profile and print an error rather than deleting directories while WebKit may still write. - Caveat: separate runner processes for the same hostname share the same profile. Multiple URLs in one invocation currently use a combined profile key. Decide whether either sharing behavior is acceptable. ### Privacy lock and navigation - The title bar has a **Lock** button. It applies an opaque, input-blocking PIN overlay to every runner window while retaining live WebKit views. A correct PIN resumes exactly the same page state without a reload. This is a convenience privacy lock, not a security boundary against the unlocked OS account or process inspection. - While privacy-locked, back, forward, reload/stop, and the address field are disabled. Their pre-lock navigation state is recomputed on unlock. - Shortcuts: `Ctrl+Shift+L` locks all runner windows; `Ctrl+L` is captured at GTK's capture phase before WebKit and focuses/selects the active address bar. The previous application-accelerator-only implementation did not work when WebKit had focus; `06ab347` changed this. The final Ctrl+L change still needs a manual desktop confirmation. - Browser windows use GTK `HeaderBar`. The runner watches GNOME's `org.gnome.desktop.interface color-scheme` and sets GTK's dark preference; this fixed a light header on a GNOME `prefer-dark` desktop. - Address, back, and forward state are refreshed from `load-changed` and `notify::uri`, `notify::can-go-back`, and `notify::can-go-forward` to support History API / SPA navigation that does not emit a full page load. - The runner automatically privacy-locks after five minutes without pointer or keyboard activity. `Ctrl+Shift+L` remains the manual lock shortcut. - Credentials use `Ctrl+Shift+P` for the active browser window. The Credentials icon and shortcut are omitted when `pass --version` is unavailable. The Credentials window has a **Show password** toggle and is destroyed when the runner privacy-locks. ## Important implementation details ### Construct-only `network-session` `cl-gtk4`'s generated `webkit:make-web-view` does not accept the construct-only `network-session` property. `make-web-view-with-network-session` uses CFFI and `g_object_new_with_properties()` with a manually populated `GValue`. Every browser WebView must use this helper; reverting to `webkit:make-web-view` uses the shared default WebKit session. Website-data clearing also uses CFFI because the generated binding did not marshal the async callback correctly. The callback calls `webkit_website_data_manager_clear_finish()` before deleting profile files. WebKitGTK foreign libraries are loaded lazily. Loading WebKit while saving an SBCL image started foreign threads and made `program-op` crash; do not restore top-level `cffi:use-foreign-library` for WebKitGTK. ## Validation performed - `asdf:test-system :clgtk4-web-app-runner/tests` passes. Tests cover target key derivation, host parsing (ports, userinfo, IPv6, case), profile path scoping, and removal of temporary data/cache profile roots when the verifier is missing. - Standalone executable builds successfully and runs `--help` after being copied outside the checkout, without Quicklisp. - PIN directory/file permission check against a temporary XDG data directory produced `0700` and `0600`, respectively. - The executable was launched with a temporary profile after theme and shortcut initialization; it remained running without startup errors. - Runtime dependencies are documented in `README.md`: GTK4, WebKitGTK 6.0, GLib/GIO, GObject Introspection typelibs, and a Wayland/X11 display. Checkout development additionally needs SBCL, Quicklisp, and the listed Lisp systems; Credentials additionally needs pass/GnuPG, with Tomb optional. The Credentials control is hidden if `pass` is unavailable. - A user manually confirmed the privacy-lock overlay works. The new five-minute inactivity timer, credentials shortcut, password visibility toggle, and pass-availability hiding still need real-display manual confirmation. Full cleanup and CFFI WebView desktop integration remain only partially smoke-tested. ## Documentation - `README.md`: terminology, usage, PIN/profile locations, privacy-lock limits, shortcuts, build, tests, desktop-entry installation, AI-assistance disclosure, and AGPL-3.0-or-later licensing. - `docs/design-decisions.md`: critical security/isolation decisions and noncritical presentation/distribution/password-store decisions. - `docs/lock-plan.md`: threat model and distinction between privacy overlay and a future genuine security lock. - `docs/profile-isolation-plan.md`: isolation status and outstanding work. - `docs/pass-tomb-plan.md`: password-store/Tomb boundary, implemented UI, age/GnuPG boundary, and staged plan. - `data/com.ejuarezg.web-app-runner.desktop`: Freedesktop launcher for an installed executable on `PATH`. ## Recommended next steps 1. **Manual desktop regression test** - Verify automatic locking after five minutes and activity reset on motion, clicks, and keyboard input. - Verify `Ctrl+Shift+P`, the Show password toggle, and that the Credentials icon/shortcut disappear when `pass` is unavailable. - Confirm `Ctrl+L` focuses/selects the address bar while WebKit has focus. - Confirm locked windows visibly gray navigation controls and cannot navigate; unlock and verify their correct prior enabled state. - Navigate an SPA / History API site and verify URL and back/forward update immediately. - Test normal retention and clear-on-close isolation using Duck and Photos. 2. **Profile semantics** - Decide same-host concurrent-process sharing/locking behavior. - Decide whether multiple command-line URLs should deliberately share their current combined profile or instead get a session/profile per target. 3. **Robustness/tests** - Add real-display integration coverage for custom WebView creation, cleanup completion, lock controls, and keyboard shortcuts. - Add automated cleanup-path behavior tests, not only derivation/scoping. - Consider visibly reporting cleanup failure before the final window exits. 4. **Password-store hardening** - The pass credential feature is merged into main. Credential lookup/save is synchronous and can briefly block the GTK UI during GPG. Multiple-account selection and form filling remain future work. - Clipboard clearing is best-effort because clipboard managers may retain history. The runner does not unlock password-store Tombs or handle keys. 5. **Deferred features** - Private/no-storage mode via an ephemeral network session. - Confirmed “forget data now” action. - Strong lock that destroys views, idle/focus/suspend locks, keyring/PAM, and optional host allowlist.