VA-24: native assistance-mode GUI in egui (Linux first) #61

Merged
nrupard merged 2 commits from feat/VA-24-agent-gui into main 2026-06-13 16:31:49 +02:00
Owner

Summary

Linux-first slice of the native assistance-mode GUI (VA-24). Adds a native, in-process agent UI plus an in-process consent dialog that replaces the zenity/kdialog shell-out when a GUI is attached. macOS and Windows window/capture backends are split into follow-ups VA-55 and VA-56.

Decisions (resolved with the maintainer before coding)

  • Invocation: a separate per-user vervain-agent assistance subcommand (not daemon auto-launch), talking to the daemon over a local unix socket.
  • Session model: the GUI is a per-user process; the VA-23 system/root daemon has no display, so it keeps returning ConsentOutcome::NoUi until a GUI connects. This reconciles the GUI with the hardened system-service default.
  • Scope: Linux (X11 + Wayland) only in this PR, since macOS/Windows cannot be built or runtime-validated in CI.

What landed

  • New crates/vervain-agent-gui crate: an egui/eframe app (status window + consent dialog), a gtk system-tray icon on its own main-loop thread showing connection state (disconnected / connected / operator-attached) with a "disconnect now" item, desktop notifications on operator attach, and an async IPC client. The newline-delimited JSON IPC contract lives in a dependency-light proto module so the daemon links just the wire types, not eframe.
  • crates/vervain-agent/src/status.rs: process-global connection status (state watch + active-tunnel count + a disconnect signal), wired into the connection loop (net::run_reconnecting/idle_loop) and the tunnel spawner (host::tunnel::spawn).
  • crates/vervain-agent/src/gui_ipc.rs (behind agent-gui): a unix-socket server that pushes status to the GUI, routes consent prompts to it and awaits the reply, and handles "disconnect now" by dropping the server connection (which tears down every active tunnel, since tunnels die with the relay).
  • host/consent.rs: prefers the attached GUI over IPC, falling back to the existing host helpers and ultimately NoUi, so headless installs are unchanged.
  • agent-gui feature is off by default; the standard build stays headless.

CI

  • The standard --workspace lanes build the new crate (it links the egui/gtk stack), so the desktop dev libs are added to the check image.
  • A dedicated lane clippy+builds --features agent-gui.
  • vervain-agent-gui is excluded from the windows-gnu cross-check until its Windows backend lands (VA-56).
  • Full check image builds green: fmt, clippy/build/test --workspace, windows-gnu cross-check, the agent-gui lane, and the existing swtpm TPM lane.

Acceptance criteria

  • vervain-agent-gui workspace member builds on Linux (X11 + Wayland).
  • System tray shows connection state and a "disconnect now" item.
  • In-process egui consent dialog serves host/consent.rs over IPC, removing the shell-out when a GUI is attached.
  • agent-gui feature cleanly excludes the GUI for headless builds; default stays headless.
  • Operator-connected notification fires, with a "Disconnect" action wired back to the daemon.
  • Stripped Linux release binary is 16 MB (under the 20 MB target).
  • ROADMAP updated; VA-18 marked superseded.
  • macOS / Windows builds + capture exclusion (NSWindowSharingNone / WDA_EXCLUDEFROMCAPTURE): deferred to VA-55 / VA-56.

Not runtime-tested here

The tray, consent dialog, and notifications compile and pass unit tests (IPC proto round-trip, status transitions) but cannot be exercised in headless CI (no display / tray). Runtime validation on a real Linux desktop is a manual step, like the deferred macOS/Windows GUI work.

Follow-ups

  • VA-55: macOS assistance-GUI backend (status item, consent, NSWindowSharingNone).
  • VA-56: Windows assistance-GUI backend (named-pipe IPC, Session 0 launch, WDA_EXCLUDEFROMCAPTURE).

#VA-24

## Summary Linux-first slice of the native assistance-mode GUI (VA-24). Adds a native, in-process agent UI plus an in-process consent dialog that replaces the `zenity`/`kdialog` shell-out when a GUI is attached. macOS and Windows window/capture backends are split into follow-ups VA-55 and VA-56. ## Decisions (resolved with the maintainer before coding) - Invocation: a separate per-user `vervain-agent assistance` subcommand (not daemon auto-launch), talking to the daemon over a local unix socket. - Session model: the GUI is a per-user process; the VA-23 system/root daemon has no display, so it keeps returning `ConsentOutcome::NoUi` until a GUI connects. This reconciles the GUI with the hardened system-service default. - Scope: Linux (X11 + Wayland) only in this PR, since macOS/Windows cannot be built or runtime-validated in CI. ## What landed - New `crates/vervain-agent-gui` crate: an egui/eframe app (status window + consent dialog), a gtk system-tray icon on its own main-loop thread showing connection state (disconnected / connected / operator-attached) with a "disconnect now" item, desktop notifications on operator attach, and an async IPC client. The newline-delimited JSON IPC contract lives in a dependency-light `proto` module so the daemon links just the wire types, not eframe. - `crates/vervain-agent/src/status.rs`: process-global connection status (state watch + active-tunnel count + a disconnect signal), wired into the connection loop (`net::run_reconnecting`/`idle_loop`) and the tunnel spawner (`host::tunnel::spawn`). - `crates/vervain-agent/src/gui_ipc.rs` (behind `agent-gui`): a unix-socket server that pushes status to the GUI, routes consent prompts to it and awaits the reply, and handles "disconnect now" by dropping the server connection (which tears down every active tunnel, since tunnels die with the relay). - `host/consent.rs`: prefers the attached GUI over IPC, falling back to the existing host helpers and ultimately `NoUi`, so headless installs are unchanged. - `agent-gui` feature is off by default; the standard build stays headless. ## CI - The standard `--workspace` lanes build the new crate (it links the egui/gtk stack), so the desktop dev libs are added to the check image. - A dedicated lane clippy+builds `--features agent-gui`. - `vervain-agent-gui` is excluded from the windows-gnu cross-check until its Windows backend lands (VA-56). - Full check image builds green: fmt, clippy/build/test `--workspace`, windows-gnu cross-check, the `agent-gui` lane, and the existing swtpm TPM lane. ## Acceptance criteria - [x] `vervain-agent-gui` workspace member builds on Linux (X11 + Wayland). - [x] System tray shows connection state and a "disconnect now" item. - [x] In-process egui consent dialog serves `host/consent.rs` over IPC, removing the shell-out when a GUI is attached. - [x] `agent-gui` feature cleanly excludes the GUI for headless builds; default stays headless. - [x] Operator-connected notification fires, with a "Disconnect" action wired back to the daemon. - [x] Stripped Linux release binary is 16 MB (under the 20 MB target). - [x] ROADMAP updated; VA-18 marked superseded. - [ ] macOS / Windows builds + capture exclusion (`NSWindowSharingNone` / `WDA_EXCLUDEFROMCAPTURE`): deferred to VA-55 / VA-56. ## Not runtime-tested here The tray, consent dialog, and notifications compile and pass unit tests (IPC proto round-trip, status transitions) but cannot be exercised in headless CI (no display / tray). Runtime validation on a real Linux desktop is a manual step, like the deferred macOS/Windows GUI work. ## Follow-ups - VA-55: macOS assistance-GUI backend (status item, consent, `NSWindowSharingNone`). - VA-56: Windows assistance-GUI backend (named-pipe IPC, Session 0 launch, `WDA_EXCLUDEFROMCAPTURE`). #VA-24
feat(gui): native assistance-mode GUI in egui, Linux first (VA-24)
All checks were successful
Check / clippy + fmt + tests (pull_request) Successful in 4m30s
da2f590ca7
Add a native, in-process agent GUI for "assistance mode" plus an in-process consent dialog, replacing the zenity/kdialog shell-out when a GUI is attached. Linux-first slice; macOS and Windows window/capture backends are tracked as follow-ups (VA-55, VA-56).

New `vervain-agent-gui` crate: an egui/eframe app (status window + consent dialog), a gtk system-tray icon on its own main-loop thread showing connection state with a "disconnect now" item, desktop notifications on operator attach, and an async IPC client. The newline-delimited JSON IPC contract lives in a dependency-light `proto` module so the daemon can link just the wire types.

Daemon side (behind the off-by-default `agent-gui` feature): a process-global `status` module (connection state + active-tunnel count + a disconnect signal) wired into the connection loop and tunnel spawner; a `gui_ipc` unix-socket server that pushes status to the GUI, routes consent prompts to it and awaits the reply, and handles "disconnect now" by dropping the server connection (which tears down every active tunnel); and a per-user `assistance` subcommand that runs the GUI in the desktop session and talks to the daemon over the socket. When no GUI is attached, consent falls back to the existing host helpers and ultimately `NoUi`, so headless installs are unchanged.

The per-user-process + IPC design reconciles the GUI with the VA-23 system/root daemon, which has no display: the daemon keeps returning `NoUi` until a GUI connects.

CI: the standard `--workspace` lanes build the crate (it links the egui/gtk stack), so the desktop dev libs are added to the check image. A dedicated lane clippy+builds `--features agent-gui`. The crate is excluded from the windows-gnu cross-check until its Windows backend lands. ROADMAP updated; VA-18 marked superseded.

#VA-24

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix(gui): address VA-24 review findings
All checks were successful
Check / clippy + fmt + tests (pull_request) Successful in 4m16s
Create release / Create release from merged PR (pull_request) Has been skipped
2f2a9218d3
Make "disconnect now" actually sever the operator. Operator tunnels are detached tasks connected to the relay, independent of the control connection, so dropping control alone left the live desktop/terminal session running. `status` now records each tunnel task's abort handle (pruning finished ones); `request_disconnect` aborts every live tunnel before dropping control.

Create the IPC socket directory on a real system install. The system unit runs as the unprivileged `vervain-agent` user, which cannot mkdir under root-owned `/run`, so the IPC server silently failed to bind. Add `RuntimeDirectory=vervain-agent` to the unit so systemd creates `/run/vervain-agent` owned by the service user.

Tighten the socket from world-writable (0o666) to group-only (0o660) owned by the daemon's group, so an arbitrary local user can no longer answer consent prompts or trigger disconnects; the desktop user connects via the daemon's group.

Return the disconnect path as a clean close so `run_reconnecting` resets its backoff and reconnects promptly instead of counting a deliberate disconnect as a failure. Use `Notify::notify_one` (stores a permit) so a click made while the loop is mid-frame is not lost. Give the operator-attached notification a 20s timeout so its action-wait thread exits instead of blocking for the process lifetime.

#VA-24

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
nrupard deleted branch feat/VA-24-agent-gui 2026-06-13 16:31:49 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/vervain-agent!61
No description provided.