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 + locks the stranded session via tscon -- then verifies the reconnect actually held (a fully hardened LSM block can't be cleared by tscon; the watchdog reports that instead of a false success).

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 -- never a sign-out, so every process keeps running -- and leaves it locked, so a recovered box is never an open desktop. 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.

tscon is not a cure-all, and wtf-rdp is clear about it. After reconnecting, the watchdog verifies the reconnect actually held. A lightly-wedged session reconnects and stays put -- a real recovery. But a session under a fully hardened LSM block reconnects to the console and then decays back to disconnected within a couple of minutes: tscon cannot clear that, and only a reboot (or preventing the wedge up front) will. wtf-rdp reports which case it hit -- recovered (held) vs. reconnected but decayed (hardened block -> reboot needed) -- rather than claiming a success that didn't stick.

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.3.tar.gz (45.5 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.3-py3-none-any.whl (47.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: wtf_rdp-0.3.3.tar.gz
  • Upload date:
  • Size: 45.5 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.3.tar.gz
Algorithm Hash digest
SHA256 b384acc9501308262e6e0476a03aa34802b7c6e946c6116eaf66c5c02fcd8856
MD5 d604f2c6705c9ec7ba0b26e50c2d08fb
BLAKE2b-256 239ac3da7d992b3bcce3a392c9312cbf3a737511126168ca80f5c9957e2eb37b

See more details on using hashes here.

Provenance

The following attestation bundles were made for wtf_rdp-0.3.3.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.3-py3-none-any.whl.

File metadata

  • Download URL: wtf_rdp-0.3.3-py3-none-any.whl
  • Upload date:
  • Size: 47.3 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.3-py3-none-any.whl
Algorithm Hash digest
SHA256 2175c0cf60abdc5626eaa9b704dac99c585092fadd411168aaf02d4c2164cba9
MD5 aa2a37b7aa085bf88a752f80dec9af70
BLAKE2b-256 48ce0f506dcb0bc70d959f72a4a25688a263f58575969b07300630d12b2f7c8b

See more details on using hashes here.

Provenance

The following attestation bundles were made for wtf_rdp-0.3.3-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