Skip to main content

Windows RDP session-rescue tooling: a wtf-windows-family CLI (rdp) built on dazzlecmd-lib that detects and non-destructively recovers RDP sessions wedged/blocked by Local Session Manager.

Project description

wtf-rdp (rdp)

PyPI Release Date Python 3.10+ License: GPL v3 Installs Platform

Rescue the RDP session Windows would make you destroy.

Windows RDP session-rescue tooling. A wtf-windows-family CLI (rdp) built on the DazzleCMD pattern that detects and non-destructively recovers RDP sessions wedged or blocked by Local Session Manager (LSM). wtf-rdp handles the "You're unable to sign in because you're already signed in to another session that is blocked" failure that otherwise forces you to sign the session out and lose your work.

Why wtf-rdp?

Have you ever RDP'd into a machine, had the connection drop ungracefully, only to find yourself locked out of your own session with Windows cheekily offering one option suggesting to sign-out (and destroy) the very session holding your unsaved work?

That's the Local Session Manager block. An ungraceful mstsc disconnect can flag your session "blocked by Local Session Manager" after which Windows refuses every sign-in (RDP and from the physical console); and the only button it gives you is the one that throws your work away (hence "wtf rdp?"). On client Windows (single interactive session), you can't even spin up a second session to fix it. Also the block counter climbs and never expires.

Enter wtf-rdp...

wtf-rdp installs a tiny SYSTEM watchdog service (hosted by NSSM as LocalSystem) that watches for the wedge signature (a console session stuck connecting, corroborated by the LSM transition-failure event) and reconnects the stranded session via tscon before Windows can garbage-collect it.

Features

  • Non-destructive recovery: reconnects the blocked session via tscon -- it never signs it out, so your running work survives
  • Autonomous watchdog: a SYSTEM service detects the wedge and rescues it on its own, before Windows can destroy the session
  • No OS changes required: runs as LocalSystem; no LocalAccountTokenFilterPolicy, no enabled built-in Administrator, no weakened token model
  • Event-driven detection: keys on the real LSM wedge signature (not just "disconnected"), so it acts on genuine blocks and stays silent on normal idle disconnects
  • Real Windows service: NSSM hosts the watchdog as a LocalSystem service you can query and control with standard tooling (Get-Service, services.msc); rdp install fetches a checksum-verified NSSM, so the PyPi wheel stays pure Python (no bundled binaries)
  • DazzleCMD aggregator: rdp <tool> [args] works anywhere; tools grow as additional kits without changing the CLI

Installation

Which machine? Install on the RDP host (the machine you connect into, i.e. the one that keeps getting borked). The machine you connect from just uses Remote Desktop / mstsc as usual; you don't install wtf-rdp there. The watchdog acts on its own as SYSTEM on the host, so recovery needs no remote access or sign-in. (Every rdp command runs against the local host today; a remote query/recover path from the client is a possible future feature.)

pip install wtf-rdp
rdp install              # deploy + start the LocalSystem watchdog service

On externally-managed Python (PEP 668), install into a virtual environment or use pipx:

pipx install wtf-rdp

Or install from source:

git clone https://github.com/djdarcy/wtf-rdp.git
cd wtf-rdp
pip install -e .

Getting Started

After installing, here's a first run, start to finish. Do this in an elevated PowerShell — the watchdog installs as a LocalSystem service:

# 1. Deploy + start the watchdog, scoped to just your account so it only ever
#    acts on your session. (Omit -TargetUser to rescue any stranded session.)
rdp install -TargetUser $env:USERNAME

# 2. Confirm it's running as SYSTEM and see the live session table
rdp status

That's the whole setup. The watchdog now runs across reboots and will reconnect + lock your session if it wedges (LSM block / dirty disconnect). No further action from you.

Day-to-day, once it's installed:

rdp status                     # is the watchdog running? what do sessions look like?
rdp query                      # deeper session table; flags stranded / wedge candidates
rdp logs -Follow               # watch the watchdog live (Ctrl-C to stop)
rdp recover                    # force a reconnect + lock right now, instead of waiting
rdp config -PollIntervalSec 10 # tune how often it checks (restarts the service)

To remove everything:

rdp uninstall -Purge           # stop + remove the service & delete C:\ProgramData\wtf-rdp

Usage

# List available tools
rdp list

# Watchdog service state, recent rescues, and the live session table
rdp status

# install / manage the watchdog and recover a wedged session
rdp install               # deploy + start the LocalSystem watchdog service
rdp query                 # enumerate sessions; flag stranded / wedge candidates
rdp recover               # manual one-shot safe rescue (tscon + lock)
rdp logs                  # view the watchdog log (-Follow to stream)
rdp config                # show / change poll interval, confirm window, target user
rdp uninstall             # stop + remove the service

# Detailed info about a tool
rdp info status

# Version
rdp --version

rdp is the short command; tools live in the root namespace (rdp status), with full FQCN addressing (wtf-rdp:sessfix:status) also available.

Included Tools

wtf-rdp ships one always-active kit today, with more categories possible in the future:

  • sessfix (always active) -- session-fix tools for the LSM block: status, query, install, recover, logs, config, uninstall (shipping)
  • diagnose, keepalive (future kits) -- attach as additional kits without changing the CLI

Run rdp list to see what's active on your machine.

How It Works

wtf-rdp targets one specific Windows failure and recovers from it without destroying your session.

The failure. When an RDP connection drops ungracefully, Windows can leave the session in a corrupted state that we call a "wedge" and Local Session Manager flags it "blocked." From then on every sign-in (RDP and the physical console) is refused, and the only recovery Windows offers is to sign the session out, which kills everything running in it. The block never times out on its own, and on client Windows (single interactive session) you can't even open a second session to fix it.

Detection. A wedge looks, to a naive check, exactly like a healthy machine sitting at its login screen -- so wtf-rdp does not act on "a session is disconnected." It watches for a wedge signature: a console session stuck in the connecting state, corroborated by the Local Session Manager transition-failure event a dirty disconnect emits. It then waits a short confirm window; transitions that resolve on their own are ignored, so a normal idle disconnect never triggers a rescue.

Recovery. Once a wedge is confirmed, wtf-rdp reconnects the stranded session with tscon. wtf-rdp reconnects, never signs out, so every process in the session keeps running. The rescue runs as SYSTEM because reconnecting another user's session needs SeTcbPrivilege, which only SYSTEM holds. This is why nothing about the machine's admin accounts or token policy has to change. Immediately after reconnecting, the session is left locked (not an open desktop), so a recovered box is never left exposed.

Where it runs. The rescue logic is a small PowerShell watchdog (Watch-RdpSession.ps1) hosted by NSSM as a LocalSystem service, so it keeps watching across reboots and can act even when nobody is logged in. rdp install installs it; rdp status / rdp recover drive it.

Want to add your own RDP tools or a new kit? See Extending wtf-rdp.

Project Structure

wtf-rdp is a thin aggregator over the dazzlecmd-lib engine (discovery, dispatch, kit/state machinery), installed as a dependency.

wtf-rdp/
├── wtf_rdp/                     # the `rdp` CLI package
│   ├── cli.py                   # AggregatorEngine entry point (command = rdp)
│   ├── _version.py
│   ├── lib/                     # shared helpers (Python-first; a shared PS module lands as tools grow)
│   ├── kits/
│   │   └── sessfix.kit.json     # the sessfix tool list
│   ├── tools/sessfix/           # per-tool manifests + scripts
│   │   └── status/              # .wtf-rdp.json + status.ps1
│   └── assets/                  # bundled scripts: Watch-RdpSession.ps1, Invoke-RdpConnect.ps1
├── docs/                        # platform support + guides
├── tests/
└── scripts/                     # repokit-common: version management + git hooks

Platform

Windows only. wtf-rdp targets a Windows RDP / Local Session Manager failure, so the runtime tooling (watchdog, tscon, qwinsta) runs only on Windows.

Platform Status
Windows 11 (24H2 / 26200) Tested -- watchdog rescue validated
Windows 10 Expected (same session / WTS / tscon / LSM surfaces)
Windows Server + RDS Expected (multi-session; the client single-session limit does not apply)

The rdp CLI is Python and will install elsewhere, but its sessfix tools no-op / warn off-Windows. See Platform Support.

Documentation

Related Projects

  • wtf-windows -- "Many diagnostics, one command": the Windows-diagnostics CLI family wtf-rdp belongs to
  • dazzlecmd -- "A tool for tools": the aggregator pattern wtf-rdp is built on
  • dazzlecmd-lib -- the engine (discovery, dispatch, kit/aggregator/state machinery) that rdp runs on
  • git-repokit -- the standardized repository scaffolding this project was created with

Contributing

Contributions are welcome! Please read our Contributing Guide for details.

Like the project?

"Buy Me A Coffee"

License

Copyright (C) 2026 Dustin Darcy

This project is licensed under the GNU General Public License v3.0 -- see the LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

wtf_rdp-0.3.1.tar.gz (42.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

wtf_rdp-0.3.1-py3-none-any.whl (42.5 kB view details)

Uploaded Python 3

File details

Details for the file wtf_rdp-0.3.1.tar.gz.

File metadata

  • Download URL: wtf_rdp-0.3.1.tar.gz
  • Upload date:
  • Size: 42.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for wtf_rdp-0.3.1.tar.gz
Algorithm Hash digest
SHA256 2ae242a783f1782d002899ce987ed1905ce6227af5d1ea645fe4c1044123036b
MD5 dceb035355d60f66893d092db27564f4
BLAKE2b-256 730846fd8815aebfc0f065f44afd0932f1ddc8ca51518017975cc99bc0f1743e

See more details on using hashes here.

Provenance

The following attestation bundles were made for wtf_rdp-0.3.1.tar.gz:

Publisher: release.yml on djdarcy/wtf-rdp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file wtf_rdp-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: wtf_rdp-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 42.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for wtf_rdp-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 9441001eaa94304fe74253a6ab0800808b5a9a8c092369409432c3ae9a1ffb70
MD5 fe39aff63a1c49de6aea4be4d896633b
BLAKE2b-256 5b4889948e855a18c83aa152bdef202a44685655fb825539fbef507305d43136

See more details on using hashes here.

Provenance

The following attestation bundles were made for wtf_rdp-0.3.1-py3-none-any.whl:

Publisher: release.yml on djdarcy/wtf-rdp

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page