diff --git a/docs/FEATURE_ROADMAP.md b/docs/FEATURE_ROADMAP.md index df62802..bc9df25 100644 --- a/docs/FEATURE_ROADMAP.md +++ b/docs/FEATURE_ROADMAP.md @@ -70,6 +70,7 @@ Bringing GC to parity with GuruRMM's release engineering. Full plan: [SPEC-001]( - [ ] **Headless Linux mode (direct TTY access)** — P2 — Terminal-based remote access for Linux servers without GUI. PTY spawn (`openpty`), xterm.js web viewer, full ANSI/VT100 support. Enables server management, container debugging, emergency recovery via GuruConnect dashboard with audit logging. SSH replacement with centralized auth. ([SPEC-012](specs/SPEC-012-headless-linux-tty.md)) - [ ] **Managed-agent SYSTEM service host + session broker** — P1 — convert the persistent agent from `HKCU Run` (user context) to a LocalSystem **service** that runs unattended (login screen, no user, across reboots) and spawns a per-session capture/input worker into the active desktop (Session 0 can't capture directly). Unblocks SPEC-016 Phase B end-to-end (the SYSTEM-ACL'd `cak_` store becomes readable; removes the Phase B fail-fast guard), enables true unattended access, and is the **broker primitive SPEC-013 builds on**. ([SPEC-018](specs/SPEC-018-managed-agent-service-host.md)) - [ ] **Windows session selection and backstage mode** — P2 — Enumerate and switch between Windows user sessions (Terminal Services/RDP/Fast User Switching) and access Session 0 (backstage) for system-level admin tasks. ScreenConnect parity: session selector shows all logged-on users, instant switching without reconnect. Backstage mode provides terminal/command interface for services management without disrupting any user desktop. Critical for multi-user server environments. ([SPEC-013](specs/SPEC-013-session-selection-and-backstage.md)) +- [ ] **Private Backstage session — GUI private desktop (interactive uninstall)** — P2 — extend backstage (SPEC-013, terminal-only) to a private GUI **desktop**: run an arbitrary program (e.g. a stubborn uninstaller) on a hidden `CreateDesktop` desktop, capture it (GDI; DXGI can't target an off-screen desktop) and inject input bound to it, streamed only to the tech — the logged-on user sees nothing. Builds on SPEC-018 (service host/broker). Deep-linked from the GuruRMM "Needs remote removal" flag (RMM SPEC-030 Tier-2). ([SPEC-019](specs/SPEC-019-private-backstage-session.md)) - [ ] **Configurable notification overlay on viewer connection** — P2 — Display a semi-transparent on-screen notification when a technician connects, showing technician name and company. Dashboard-configurable message template (supports `{{technician_name}}`, `{{company}}`, `{{time}}`), duration (5-60s), position (top-left/right, bottom-left/right, center), and dismissible behavior. Increases transparency and user awareness during remote support sessions. Compliance-friendly for privacy policies requiring user notification. ([SPEC-015](specs/SPEC-015-notification-overlay.md)) - [ ] Multi-monitor switching — P2 - [ ] File transfer — P3 (out of scope for native-remote-control v1) diff --git a/docs/specs/SPEC-019-private-backstage-session.md b/docs/specs/SPEC-019-private-backstage-session.md new file mode 100644 index 0000000..482bcab --- /dev/null +++ b/docs/specs/SPEC-019-private-backstage-session.md @@ -0,0 +1,119 @@ +# SPEC-019: Private Backstage Session (interactive uninstall / hidden desktop) + +**Status:** Proposed +**Priority:** P2 +**Requested By:** Howard (2026-06-22) +**Estimated Effort:** Large + +## Overview + +A **private session** mode for GuruConnect: the tech gets an interactive remote view of a +process's UI on the target machine that the **logged-on user does not see**. The motivating use +case is GuruRMM's Tier-2 software removal — when a program has no silent uninstall (flagged +`needs_remote` by the RMM removal engine), the tech opens a Backstage session, the stubborn +uninstaller's GUI runs on a **private desktop**, and the tech clicks through it remotely while the +end user's screen is undisturbed. This is GuruConnect's equivalent of ScreenConnect Backstage. + +**Success criteria:** +- A tech can launch a program (e.g. an uninstaller) on a target and drive its UI remotely. +- The logged-on user does not see the window or the interaction (private-desktop mode). +- Works with no interactive user logged in (system-spawned private desktop). +- Deep-linkable from the GuruRMM "Needs remote removal" flag. + +## The Windows constraint (why this is non-trivial) + +A GUI process needs an interactive **window station + desktop**. The agent runs as SYSTEM in +**Session 0**, which is isolated and cannot present UI. So GuruConnect must either (a) run the +target process on the **active user's desktop** and mirror it (visible to the user), or (b) create +a **separate private desktop** (`CreateDesktop`) and stream only that desktop to the tech +(invisible to the user). (b) is the Backstage model. + +## Scope + +### Included in v1 +- **Private-desktop capture/input:** capture a *specific* desktop's framebuffer and inject + input into it (not just the active console desktop), via a dedicated window station/desktop the + agent creates (`CreateWindowStation`/`CreateDesktop` + `SetThreadDesktop` on the capture/input + threads). +- **Launch-process-in-session:** start an arbitrary command (the uninstaller) bound to that + private desktop, as SYSTEM or as a chosen user token. +- **Backstage viewer mode** in the web/native viewer: a session flagged "private" with a clear + "user cannot see this" indicator. +- **GuruRMM deep link:** accept a launch target (program path/uninstall string) so the RMM + `needs_remote` action opens straight into a Backstage session pointed at that program. + +### Explicitly out of scope +- Mirroring the *active user* desktop — that is ordinary remote control (already covered); this + spec is specifically the **private/hidden** desktop. +- Logging in a brand-new interactive user session (secondary-logon) — heavier and fragile; + considered in Future Considerations only. +- The RMM-side removal engine + tracking (lives in GuruRMM SPEC-030; this is the GC counterpart). + +## Architecture + +- **agent (Windows):** new private-desktop subsystem — create a window station + desktop, run the + capture loop with `SetThreadDesktop` bound to it (DXGI won't capture an off-screen desktop, so + fall back to GDI `BitBlt`/`PrintWindow` for the private desktop), inject input with + `SendInput`/`PostMessage` targeted at that desktop's windows. Process launch via + `CreateProcessAsUser`/`CreateProcess` with `STARTUPINFO.lpDesktop` set to the private desktop. +- **relay-server (Axum):** a new session kind `private`/`backstage`; same protobuf transport, + flagged so the dashboard/viewer render the "hidden from user" state and audit it distinctly. +- **viewer:** render the private-desktop stream; banner indicating the user can't see it. +- **dashboard:** "Start Backstage session" entry; accept the deep-link launch target. +- **proto (`proto/guruconnect.proto`):** add a session-mode enum (`NORMAL`/`PRIVATE`) and a + `LaunchProcess { command, desktop: PRIVATE, run_as }` control message. + +## Implementation details + +- Capture: DXGI Desktop Duplication is tied to the active output/desktop; a private off-screen + desktop is **not** duplicatable that way — use GDI capture of the private desktop's windows + (`PrintWindow` per top-level window or `BitBlt` of the desktop DC after `SetThreadDesktop`). + Lower FPS is acceptable for clicking through an installer. +- Input: `SendInput` operates on the calling thread's desktop — bind the input thread with + `SetThreadDesktop(hPrivateDesktop)` before injecting; for stubborn controls use + `PostMessage`/`SendMessage` to the target HWND. +- Lifecycle: tear down the private desktop + window station when the session ends; kill orphaned + child processes bound to it. + +## Security considerations +- A private/hidden session that the end user cannot see is **powerful and must be audited + loudly** — record start/stop, operator, target, and the launched command. Consider an + org/policy toggle to require consent or to disallow private mode per tenant. +- Reuse GuruConnect's existing auth (JWT/support code/agent key). Launching arbitrary processes + as SYSTEM on a private desktop is high privilege — gate behind tech-role auth. +- Threat model: a hidden remote session is an attractive abuse target; treat parity with the + existing remote-control trust boundary plus extra audit. + +## Testing strategy +- Unit: desktop/window-station create+destroy; thread-desktop binding. +- Manual: launch Notepad on a private desktop, confirm the logged-on user does NOT see it and the + tech can type into it; then a real GUI uninstaller (e.g. an NVIDIA/Office leftover) driven to + completion; then with no user logged in. +- Integration: RMM `needs_remote` deep-link opens a Backstage session at the right program. + +## Relationship to existing specs (read first) +- **SPEC-018 (managed-agent SYSTEM service host + session broker)** is the prerequisite — it + provides the SYSTEM service + per-session worker spawning this builds on. +- **SPEC-013 (session selection + backstage)** already defines backstage as a **terminal/command** + interface (services management). SPEC-019 **extends** backstage from terminal-only to a **private + GUI desktop** — the ability to see and click an arbitrary program's *window* (an uninstaller) + that the logged-on user cannot see. Consider folding this into SPEC-013 as its "GUI backstage" + phase if the team prefers one spec. + +## Effort estimate & dependencies +- **Large.** Depends on SPEC-018 (broker) and the existing capture/input pipeline; complements + SPEC-013 (backstage). The private-desktop GDI capture + input-desktop binding is the hard, novel + part. Unblocks GuruRMM Tier-2 interactive removal and any "do something the user shouldn't see" + support workflow. + +## Open questions +- DXGI vs GDI for the private desktop — confirm DXGI truly can't target it; measure GDI FPS. +- Run the uninstaller as SYSTEM or as the logged-on user's token on the private desktop? (Some + per-user uninstallers need the user's profile/HKCU.) +- Default posture: should private mode require explicit per-session consent or a tenant policy? + +## References +- GuruRMM SPEC-030 (remote software inventory + bulk uninstall) — the `needs_remote` flag + the + removal knowledge base that feeds this. Tier-2 is the last resort; the RMM vendor table shrinks + the set over time. +- ScreenConnect Backstage (prior art for a hidden support session).