Open-source project
rylena/sshdesk avatar
rylena/sshdesk

SSHDESK: a remote desktop that never opens a second port

A full interactive remote desktop delivered entirely through SSH and displayed directly in your terminal.

531 stars29 forksPythonMIT

At a glance

What is it?
rylena/sshdesk is an MIT licensed Python tool that renders an interactive remote desktop inside your terminal, delivered through one SSH session with no extra ports, no VNC or RDP listener and no web server. The security model is the part to understand: anyone who can authenticate sees and controls the active graphical session.
Who is it for?
Use SSHDESK when you need to see and control a machine's graphical desktop and opening another port or running another daemon is the thing you are trying to avoid, since everything travels over one SSH PTY with the authentication you already have. It is strongest on Linux hosts, and sharp only on Kitty, Ghostty or WezTerm clients, so expect the lower-resolution colour-cell renderer everywhere else.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 40 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What travels over the one connection

SSHDESK is a full interactive remote desktop delivered entirely through an SSH session and displayed inside your terminal. The README states that OpenSSH authenticates the user and launches SSHDESK as a forced command, and that the active graphical desktop then appears in that same terminal.

Everything rides on the one SSH PTY: keyboard, mouse, resize events, changed pixels and session cleanup. The README is explicit about what does not exist: no browser, no custom SSH client, no VNC or RDP listener, no second password database, no web server and no additional network port.

That is the entire pitch. Conventional remote desktop means opening a port, running a daemon, and trusting that daemon's authentication in addition to your system's. This reuses the SSH access you already have, already audit and already restrict.

The install target is the host you want to see, and any OS can be the client, since the client is just an SSH client.

Sharp tiles on some terminals, colour cells everywhere else

Rendering adapts to what the terminal supports, and the difference is large.

Kitty, Ghostty and WezTerm receive sharp real-pixel tiles, described in the README as palette-compressed PNG tiles through the Kitty graphics protocol, including tmux passthrough. Every ordinary ANSI terminal receives a lower-resolution colour-cell renderer, which is what keeps OpenSSH, PuTTY, mobile clients and embedded SSH terminals usable.

So the same server gives a sharp desktop on a modern GPU terminal and a coarse one on a phone SSH client. The README gives active targets of 60 FPS for the sharp path and 30 FPS for ANSI, with adaptive idle presentation, and says the design uses latest-frame scheduling that drops stale work rather than accumulating latency.

Bandwidth is handled by sending changed tiles or cells with static-frame suppression, plus true-colour, 256-colour, 16-colour, Unicode and ASCII fallbacks. Live instrumentation reports FPS, latency, capture, diff, bandwidth and updates.

If you have never used a Kitty-protocol terminal, expect the ANSI fallback and judge it on its own terms.

Installing and connecting

On Linux or macOS the bootstrap is one line, and the README notes it downloads the same release and selects the native installer automatically:

bash
curl -fsSL https://raw.githubusercontent.com/rylena/sshdesk/main/scripts/install.sh | sh

Windows uses a PowerShell equivalent that requests Administrator permission when needed, and both entry points detect the OS, install missing Python and OpenSSH prerequisites, validate graphical access and the forced-command configuration, and start the platform's OpenSSH service.

For unattended installs the README gives the downloaded-script form with flags:

bash
sh /tmp/sshdesk-install.sh --user alice --no-tailscale

Windows PowerShell accepts -Tailscale or -NoTailscale. Tailscale is offered only after SSHDESK and OpenSSH setup succeeds, and the README is clear that it carries normal OpenSSH over the private tailnet rather than replacing it or adding a second authentication mode.

Connecting is then ordinary SSH, with the desktop as the default and a shell available explicitly:

bash
ssh [email protected]
ssh -t [email protected] shell

The README adds a warning worth repeating: a one-line installer executes downloaded code with administrator permission during setup, and the repository's install scripts should be reviewed first where that is not acceptable.

The security model is console access

The README carries a warning that should decide how you deploy this: anyone who can authenticate to an SSHDESK account can see and control the active graphical session, and you should treat it like physical console access, keeping a second administrative login available while configuring a forced command.

That is a stronger statement than most remote desktop tools make, because it is honest about what the capability means. Granting someone SSH access to a machine with SSHDESK installed is not granting a shell, it is granting their eyes and hands on your desktop.

The README also notes what installation does not change. Cross-platform installation does not remove OS security boundaries: macOS still asks for Screen Recording and Accessibility access, and any OS can be the client while Linux remains the recommended host.

Windows is the exception that proves it. Windows OpenSSH normally runs in Session 0, so Windows forced-command desktop capture is described as experimental even though the installer itself is supported.

Practical consequence: install it on hosts where every SSH user is already trusted with the console, and do not install it on a shared jump box.

Display stacks and what each one needs

The Linux capture and input matrix is where setup gets fiddly, and the README publishes a table for it.

On X11 with any desktop, capture is FFmpeg and XCB, MIT-SHM or Pillow and XCB, and input is XTest. On wlroots compositors such as Sway and Hyprland, capture is grim and input is ydotool plus ydotoold. GNOME Wayland uses a persistent Mutter plus PipeWire and GStreamer stream with the Mutter RemoteDesktop API for input. KDE Plasma Wayland uses spectacle for capture and ydotool for input.

The one-line installer handles these automatically, which is the main argument for using it rather than installing manually. The manual path needs PyGObject, GStreamer base introspection and the GStreamer PipeWire plugin on GNOME, or the listed capture command plus ydotool 1.0.4 or newer elsewhere.

One operational detail matters: non-GNOME Wayland input requires ydotoold to access /dev/uinput, and the README says not to run the whole SSHDESK server as root.

The README also documents a repair path for older installs that close with a Wayland capture error or behave like a slow screenshot slideshow: log into the graphical desktop locally, rerun the one-line command, which upgrades GNOME to the persistent PipeWire backend and preserves the existing login.

Agent use and session hygiene

Two lesser features are aimed at people running coding agents rather than at humans. The README lists agent-safe screenshot and computer-use commands carried through OpenSSH, and an optional tmux side-by-side layout giving an agent shell and the visual desktop at once.

That combination is what makes this interesting for agent workflows: the agent gets a shell and a view of the desktop over one authenticated channel, with no display server exposed to the network.

Session hygiene is handled too. The README says terminal restoration and held-input release happen after disconnects or crashes, which matters more than it sounds, since a remote desktop that leaves modifier keys stuck leaves the host in a strange state for the next person at the keyboard.

The repository is set up with tests, a CHANGELOG.md, docs/ and an AGENTS.md that instructs coding agents to read before modifying the repository. Python 3.10 or newer and a working venv are required, and the project is MIT licensed.

There are no releases yet, so installs come from main, and the last push was on 2026-08-22.

VNC and RDP as the alternative

The comparison is not with another terminal tool, it is with how remote desktops normally work.

VNC means a server listening on its own port with its own password, and a separate client. RDP means the same on Windows, with a mature protocol and better performance than most VNC implementations. Both need a port opened in a firewall, both add an authentication system to manage and audit, and both give a full graphical desktop rather than a terminal rendering.

SSHDESK needs none of that infrastructure. It works wherever port 22 already works, uses the keys and accounts you already manage, and renders in a terminal, which is a downgrade in fidelity on anything but a Kitty-protocol terminal.

Choose VNC or RDP when you want a native window, good video or audio, or users who are not terminal people. Choose SSHDESK when opening another port is the problem, when you want desktop access to inherit SSH policy, or when the operator is an agent that needs a shell and a screen over one channel.

Editorial conclusion

Use SSHDESK when you need to see and control a machine's graphical desktop and opening another port or running another daemon is the thing you are trying to avoid, since everything travels over one SSH PTY with the authentication you already have. It is strongest on Linux hosts, and sharp only on Kitty, Ghostty or WezTerm clients, so expect the lower-resolution colour-cell renderer everywhere else. Do not install it where SSH access is broader than console access should be, because the README states plainly that anyone who can authenticate can see and control the active session, and treat Windows hosts as experimental since OpenSSH runs in Session 0 there. Review scripts/install.sh before piping it to a shell, keep a second administrative login while configuring the forced command, and prefer the one-line installer over manual setup unless you want to assemble grim, ydotool and PipeWire yourself.

Frequently asked questions

Is SSH the same as remote desktop?

Not normally, since SSH gives you a shell rather than a graphical session. SSHDESK bridges the two by launching as an OpenSSH forced command and sending the desktop's changed pixels, keyboard, mouse and resize events through the same SSH PTY, with no VNC or RDP listener and no extra port.

What is sshdesk used for?

It is used to view and control a machine's active graphical desktop from any SSH client, with the desktop rendered inside the terminal, which suits cases where opening a VNC or RDP port is not acceptable.

Which terminals show the desktop sharply?

The README says Kitty, Ghostty and WezTerm receive sharp real-pixel tiles through the Kitty graphics protocol including tmux passthrough, while every ordinary ANSI terminal gets a lower-resolution colour-cell renderer.

Is SSHDESK safe to expose to any SSH user?

No. The README warns that anyone who can authenticate to an SSHDESK account can see and control the active graphical session, and says to treat it like physical console access and keep a second administrative login while configuring the forced command.

Does SSHDESK work on Windows hosts?

Partly. The README says Windows OpenSSH normally runs in Session 0, so Windows forced-command desktop capture remains experimental even though the one-line installer is supported, and Linux remains the recommended host.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. rylena/sshdesk on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/rylena-sshdesk.svg)](https://hysenlabs.com/projects/rylena-sshdesk)