Metadata-Version: 2.3
Name: remote-desktop-dashboard
Version: 2.17.6
Summary: Remote Desktop Dashboard — monitor machines and connect via Microsoft Windows App
Project-URL: Homepage, https://github.com/your-org/remote-desktop-dashboard
Project-URL: Documentation, https://github.com/your-org/remote-desktop-dashboard#readme
Project-URL: Repository, https://github.com/your-org/remote-desktop-dashboard
Author: Operations
License: MIT
Keywords: dashboard,fastapi,monitoring,rdp,remote-desktop,windows
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Web Environment
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: System Administrators
Classifier: Operating System :: Microsoft :: Windows
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: System :: Monitoring
Requires-Python: >=3.10
Requires-Dist: alembic>=1.14.0
Requires-Dist: cryptography>=43.0.0
Requires-Dist: fastapi>=0.115.0
Requires-Dist: httpx>=0.28.0
Requires-Dist: psutil>=6.1.0
Requires-Dist: pydantic-settings>=2.6.0
Requires-Dist: pydantic>=2.10.0
Requires-Dist: python-multipart>=0.0.17
Requires-Dist: pywin32>=308; sys_platform == 'win32'
Requires-Dist: sqlalchemy>=2.0.36
Requires-Dist: uvicorn[standard]>=0.32.0
Requires-Dist: wmi>=1.5.1; sys_platform == 'win32'
Provides-Extra: dev
Requires-Dist: httpx>=0.28.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
Requires-Dist: pytest>=8.3.0; extra == 'dev'
Provides-Extra: postgres
Requires-Dist: psycopg2-binary>=2.9.10; extra == 'postgres'
Description-Content-Type: text/markdown

# Remote Desktop Dashboard

A LAN-only, browser-based dashboard for monitoring and connecting to a fleet
of Windows benches over RDP. Operators see who is using each machine, reserve
one with a single click, and the dashboard hands their browser an `rdp://`
URI that opens the Remote Desktop login screen directly — no file
download, no extra modal. A small PowerShell agent on each bench locally
enforces who is allowed to RDP — including blocking local administrators
bypassing the "Remote Desktop Users" group, via per-IP firewall rules.

## What's new in 2.14

- **One-click RDP launch with native `mstsc.exe`** — the way it was meant
  to work. Click Connect → `mstsc` opens directly to the RDP login screen,
  no file in your Downloads folder. Requires running the new
  per-operator installer (`install-rdp-handler.ps1`) once on each PC —
  no admin elevation, HKCU only. See **Share -> One-click RDP launch**
  in the dashboard for the download and instructions.
- **Graceful fallback** when an operator hasn't installed the handler
  yet: the dashboard detects that nothing opened, automatically triggers
  the universal `.rdp` download so the launch still works, and surfaces
  a dismissible card explaining how to switch to the no-download flow.
- **Diagnose modal redesigned** — colour-coded summary pills, every
  check shown even when it passes (no more "verdict-only" mystery),
  "Copy report" button for pasting into a support email, "Run again"
  button without closing.
- **Admin PIN moved off the URL for two more endpoints** — push install
  command and agent diagnostics now use `X-Admin-PIN` header, so the
  PIN never lands in server access logs.
- **WebSocket reconnect no longer leaks ping timers.** Long-uptime tabs
  could end up sending pings for every dead socket they'd ever opened.
- **Native, native, native.** The handler installer explicitly points
  `rdp://` at `%WINDIR%\System32\mstsc.exe` — no Windows App,
  no msrdc, no Microsoft Store dependency.

## What's new in 2.13

- **Direct RDP launch.** Click Connect → Windows opens the RDP login screen
  immediately. No more `.rdp` file in the Downloads folder, no more
  "popup that closes before any interaction" — both were artefacts of
  the old browser-download flow. *(Note: in 2.13 this only worked if the
  operator had Microsoft Windows App / msrdc installed. From 2.14, you
  can run the per-operator installer to make it work with native
  `mstsc.exe` too.)*
- **Lock acquisition is race-safe.** Two operators clicking Connect on
  the same bench at the exact same instant can no longer both end up
  with a reservation; the loser gets a friendly 409.
- **Admin PIN moved to the `X-Admin-PIN` header.** Old `?admin_pin=`
  query strings are still accepted for compatibility, but the UI now
  sends the PIN as a header so it never lands in proxy / browser /
  SIEM access logs.
- **`/health` no longer leaks filesystem paths** to anonymous LAN
  callers. Admins still see them (pass `X-Admin-PIN`).
- **Legacy `/machines/{name}/status` is no longer open by default.**
  Requires either `RDD_AGENT_API_KEY` or `RDD_BENCH_AGENT_TOKEN`.
- **`admin.env` template default flipped:**
  `RDD_BENCH_AGENT_KICK_OTHER_SESSIONS=false`. Auto-kicking on every
  lock contradicted the documented design.
- **Constant-time secret comparison** (`hmac.compare_digest`) for the
  admin PIN, bench-agent token and legacy API key.
- **127.0.0.1 local launcher off by default** — the new `rdp://` flow
  removes the need for it. Enable with `RDD_LOCAL_LAUNCHER_ENABLED=true`.

---

## Quick start (server-side, one machine on the LAN)

```powershell
pip install remote-desktop-dashboard
remote-desktop-dashboard
```

Then open `http://localhost:8080/` in a browser on the same machine.

Default port is 8080. The first time you start it, it writes
`%LOCALAPPDATA%\RemoteDesktopDashboard\admin.env` with sensible defaults.

### Keep the dashboard running automatically

In an elevated PowerShell on the server PC:

```powershell
remote-desktop-dashboard install-autostart
```

This registers a Windows Scheduled Task (`RemoteDesktopDashboard`) that
fires every 5 minutes. The dashboard's single-instance lock makes the
duplicate fires no-op while it's already running, but if the terminal is
closed, the server crashes, or the PC reboots, the scheduler relaunches
it automatically within 5 minutes. It runs windowless (no flashing
console) via `pythonw.exe`.

Check status / inspect / remove:

```powershell
remote-desktop-dashboard service-status
remote-desktop-dashboard uninstall-autostart
```

---

## How users on OTHER PCs open the dashboard

The dashboard is just a web server on TCP **8080**. Anyone on the same LAN
who can reach the server PC can use it — **no install on their machine**.

1. On the **server PC** (the one running the dashboard), allow inbound TCP
   8080 through Windows Firewall (run in an elevated PowerShell):

   ```powershell
   New-NetFirewallRule -DisplayName "Remote Desktop Dashboard" `
     -Direction Inbound -Protocol TCP -LocalPort 8080 `
     -Action Allow -Profile Any
   ```

2. Find the server PC's LAN IP:

   ```powershell
   ipconfig | Select-String IPv4
   ```

   Or click the **share** icon in the top right of the dashboard — it shows
   every LAN URL the server is reachable on, with one-click copy.

3. From any other PC on the LAN, open
   `http://<server-pc-ip>:8080/` in a browser. That's it.

There is **no login wall** — anyone who can reach this URL can use the
dashboard. Admin-only actions (push install, force release, kick others,
emergency restore RDP, manage machines, server settings) are gated behind
the **Admin PIN** (see below).

### How the RDP launch actually works (2.13+)

When an operator clicks **Connect** on their PC, the dashboard:

1. Reserves the bench under their name (race-safe atomic UPDATE — two
   simultaneous clicks can no longer both "win").
2. Hands their browser an **`rdp://` URI** (e.g.
   `rdp://full%20address=s:atc05.lab:3389&redirectclipboard=i:1&...`).
3. The browser asks the OS to open that URI; Windows routes it directly
   to **`mstsc.exe`** (the built-in Remote Desktop client). The RDP
   **login screen opens immediately** — no `.rdp` file is downloaded,
   no extra modal pops up on the dashboard, and the dashboard tab
   stays put for the heartbeat / lock loop.
4. The first time the user clicks Connect, the browser may show a
   one-time *"Open Remote Desktop Connection?"* confirmation. Ticking
   *"Always allow"* makes future launches frictionless.

The URI sets the same defaults the `.rdp` file would have:

- Clipboard redirection (`redirectclipboard=i:1`)
- Audio playback on the operator's PC (`audiomode=i:0`)
- Smartcard redirection
- Network auto-detect + dynamic bandwidth
- Auto full-screen
- Authentication level 2 (warn but allow if cert mismatch)

A traditional **`.rdp` file fallback** is still served at
`/api/v1/machines/<name>/session.rdp` for operators who want a desktop
shortcut. It now requires `?operator=<your-name>` and only succeeds for
the operator who currently owns the lock (or an admin via
`X-Admin-PIN`), so a hand-crafted URL can't bypass a reservation.

Set `RDD_PREFERRED_RDP_CLIENT=windows_app` in `admin.env` if you want
to opt-in to the new Microsoft Windows App on operator PCs that have it.

### External RDP sessions (2.12+)

The bench agent reports **every** session it sees on the bench, not
just dashboard-initiated ones. If someone bypasses the dashboard and
RDPs directly, they show up in the machine's "Logged-in users" cell on
the dashboard within a few seconds, even when the bench is reserved by
someone else. Admins can hit **Force release** + **Kick others** to
boot them.

---

## HTTPS (remove the &ldquo;Not secure&rdquo; warning)

By default the dashboard runs over plain HTTP, so Chrome/Edge show a
**Not secure** badge in the address bar. That's harmless on a trusted LAN
but ugly. To get a real green lock instead:

1. Start the dashboard with HTTPS:

   ```powershell
   remote-desktop-dashboard --https
   ```

   On first start it generates a self-signed certificate into the data
   folder (`dashboard-cert.pem` + `dashboard-key.pem`) covering
   `localhost`, `127.0.0.1`, the machine hostname, and all detected LAN
   IPs. The same cert is reused on every subsequent start, so clients
   only install it once.

2. To make it permanent across reboots, set `RDD_HTTPS_ENABLED=true` in
   `admin.env` (next to `RDD_ADMIN_PIN`) and re-run
   `remote-desktop-dashboard install-autostart`.

3. On **each client PC** (one-time):

   - Open the dashboard, click the **share** icon, scroll to
     *"Remove the Not secure warning"* and click **Download certificate**.
   - Double-click the downloaded `.crt` file.
   - **Install Certificate** &rarr; **Local Machine** &rarr; **Place all
     certificates in the following store** &rarr; **Trusted Root
     Certification Authorities** &rarr; Finish.
   - Close and reopen the browser. The lock icon turns green.

If you already have your own real cert (e.g. issued by an internal CA),
point at it instead:

```powershell
remote-desktop-dashboard --https --cert C:\path\to\cert.pem --key C:\path\to\key.pem
```

### Why a self-signed cert is the only LAN-friendly option

Public CAs (Let's Encrypt, ZeroSSL, …) cannot issue certificates for
LAN names like `192.168.1.50` or `WORKSTATION-PC`. Until you install the
dashboard's cert as a trusted root on each client, the browser shows
*"Your connection is not private — NET::ERR_CERT_AUTHORITY_INVALID"*.
After install it shows a normal green lock.

### Desktop shortcut for users

The Share modal also offers a one-click **Download desktop shortcut**.
This is a tiny HTML file users can drop on their desktop and
double-click to open the dashboard — it auto-redirects to the right
URL/scheme.

> Earlier versions (&le; 2.11.0) used a Windows `.url` Internet Shortcut,
> which Chrome/Edge SmartScreen flagged as a **harmful download**. From
> 2.11.1 the shortcut is a plain HTML file (same UX, no warning).

---

## Roles

There are two roles, enforced server-side:

| Role         | What they can do                                                          | How they are recognized           |
| ------------ | ------------------------------------------------------------------------- | --------------------------------- |
| **Operator** | View status, connect, release their own lock, view audit log             | Anyone who opens the dashboard URL |
| **Admin**    | All of the above **plus** push-install agent, force release, kick others, emergency-restore RDP, manage machines, edit server settings | Knows the **Admin PIN** |

Most users only ever need to be operators. They just open the dashboard
URL, type their name and RDP credentials once (the server remembers them
per-name across PCs), and connect.

### Admin PIN

The Admin PIN is a single, server-wide secret set by **whoever installed
the dashboard**, in `%LOCALAPPDATA%\RemoteDesktopDashboard\admin.env`:

```
RDD_ADMIN_PIN=pick-something-only-you-know
```

Restart the dashboard after changing it.

The PIN is **not shown anywhere in the UI** for non-admins. To use it,
click the lock icon ("Admin") at the top right and type the PIN. The UI
remembers it for the current browser tab (cleared on close) and unlocks
admin-only actions. Click the icon again to sign out.

---

## Bench agent (recommended)

For each bench you want to lock, push-install the PowerShell agent from
the dashboard:

1. In `admin.env` on the server set
   `RDD_BENCH_AGENT_TOKEN=<long-random-string>` and restart the
   dashboard. (This is the shared secret the agents use to authenticate
   to the dashboard.)
2. Make sure the dashboard's monitor service account is a **local admin**
   on every bench.
3. Open the dashboard, sign in as admin, open **Settings**, scroll to
   **Bench Agent**, and click **Push install** next to each bench.

The agent:

- Manages the local "Remote Desktop Users" group based on the current
  dashboard lock.
- Manages a local Windows Firewall rule on TCP 3389 so that, when a
  bench is locked, only the lock owner's IP can RDP — this blocks even
  local administrators from RDPing directly (which would otherwise
  bypass the group check).
- Kicks unauthorized RDP sessions if they sneak in.
- Heartbeats back to the dashboard every few seconds.

### Emergency: restore native RDP on a bench

If something goes wrong with the firewall rule and you can't get back
into a bench, the admin can hit **Restore native RDP** in the dashboard
(detail panel on the right of the Status tab). This tells the agent to:

1. Remove the custom firewall lock rule.
2. Re-enable the built-in Windows "Remote Desktop" rules.
3. Release the dashboard lock.

If the agent is offline, the dashboard also shows a PowerShell snippet
you can paste on the bench from an elevated shell. It does the same
three things directly.

---

## Configuration (`admin.env`)

On Windows, `%LOCALAPPDATA%\RemoteDesktopDashboard\admin.env`.
The most relevant keys:

| Key                                | Purpose                                                          | Default      |
| ---------------------------------- | ---------------------------------------------------------------- | ------------ |
| `RDD_ADMIN_PIN`                    | The Admin PIN gating admin actions                                | unset        |
| `RDD_BENCH_AGENT_TOKEN`            | Shared secret the bench agents use to authenticate               | unset        |
| `RDD_BENCH_AGENT_FIREWALL_LOCK`    | If true (default), the agent enforces the per-IP firewall lock  | `true`       |
| `RDD_MONITOR_DOMAIN/USERNAME/PASSWORD` | Service account used to poll sessions on each bench           | unset        |

Most users never have to edit this file. The dashboard's Settings drawer
edits the monitor account fields directly; the file is updated for you.

---

## Troubleshooting

- **Machines show red even though they're online.**
  The dashboard now treats a heartbeating bench agent as proof the
  machine is online. If you've installed the agent and the dot is still
  red, open the agent's **Diagnose** button in Settings → Bench Agent;
  the most common cause is the agent failed to start (scheduled task
  `RDD-Bench-Agent` last-result != 0).

- **Chained RDP still works** (RDPing into Bench A, then RDPing from
  Bench A to Bench B). Make sure the bench agent on Bench B is running
  v2.1.0 or later (`Agent v2.1.0 online` in Inventory). The firewall
  rule is what blocks this; if it's not applied, see **Diagnose**.

- **"Invalid admin PIN."** The PIN is whatever is on the right-hand
  side of `RDD_ADMIN_PIN=` in `admin.env` on the **server**, with no
  surrounding quotes and no leading/trailing spaces.

- **I locked myself out of a bench.** Click the Admin lock icon →
  sign in → open the bench's detail panel → **Restore native RDP**.
  If the agent is offline, paste the shown PowerShell on the bench.

---

## Development

```powershell
git clone <this repo>
cd remote_desktop_dashboard
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -e ".[dev]"
pytest
```

The PowerShell agent and installer scripts ship in
`src/remote_desktop_dashboard/data/`. They are parser-checked on
every test run (`tests/test_bench_agent_scripts.py`) — silent agent
crashes from PowerShell parse errors used to be a recurring problem, so
that test gates every release.
