spec: add SPEC-019 private Backstage session (GUI private desktop for interactive uninstall)
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>
This commit is contained in:
@@ -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)
|
||||
|
||||
119
docs/specs/SPEC-019-private-backstage-session.md
Normal file
119
docs/specs/SPEC-019-private-backstage-session.md
Normal file
@@ -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).
|
||||
Reference in New Issue
Block a user