Extends SPEC-013 backstage from terminal-only to a private GUI desktop so a tech can drive a stubborn uninstaller's UI invisibly to the logged-on user. Builds on SPEC-018 broker; deep-linked from GuruRMM SPEC-030 'needs remote removal' flag. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
7.1 KiB
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+SetThreadDesktopon 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_remoteaction 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
SetThreadDesktopbound to it (DXGI won't capture an off-screen desktop, so fall back to GDIBitBlt/PrintWindowfor the private desktop), inject input withSendInput/PostMessagetargeted at that desktop's windows. Process launch viaCreateProcessAsUser/CreateProcesswithSTARTUPINFO.lpDesktopset 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 aLaunchProcess { 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
(
PrintWindowper top-level window orBitBltof the desktop DC afterSetThreadDesktop). Lower FPS is acceptable for clicking through an installer. - Input:
SendInputoperates on the calling thread's desktop — bind the input thread withSetThreadDesktop(hPrivateDesktop)before injecting; for stubborn controls usePostMessage/SendMessageto 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_remotedeep-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_remoteflag + 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).