CLI tool
sibson/vncdotool avatar
sibson/vncdotool

vncdotool: driving a VNC desktop from the command line

A command line VNC client and python library

536 stars141 forksPythonNOASSERTION

At a glance

What is it?
vncdotool is a command line VNC client and Python library that types, clicks and captures screens on a remote display, with four console entry points and a server support matrix graded by evidence rather than assumption. It suits test automation against virtual machines and headless devices, and its typing and clicking are literal key events rather than a GUI automation framework.
Who is it for?
Adopt vncdotool when you need to drive a remote desktop as part of a test or a provisioning script, especially against the servers it tests against on every pull request, since TigerVNC, x11vnc, wayvnc, LibVNCServer, QEMU's built-in server and Selenoid are regression tested.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 13 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 October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the tool is for: automating a screen that has no API

vncdotool is described as a command line VNC client, and the README explains the use case in two lines. It can be useful to automating interactions with virtual machines or hardware devices that are otherwise difficult to control.

That framing is precise, and it is the reason this kind of tool exists at all. A virtual machine that boots an image and exposes a VNC port gives you a screen and a keyboard, and nothing else. If you want to check that a boot sequence reaches a certain state, or drive an installer that has no silent options, you need something that can move a pointer, press a key and read back what the display shows. That is the whole product.

The library side of the project matters just as much. The repository description calls it a command line VNC client and python library, and the packaging declares four console scripts: `vncdo` and `vncdotool` both pointing at `vncdotool.command:vncdo`, plus `vnclog` and `vncdo-replay`. So the same automation is available as a command for scripts and as a Python API for a test suite that wants to drive a session from inside the test process.

The authors are Marc Sibson, and the acknowledgements credit Chris Liechti, techtonik and Todd Whiteman for the RFB and DES implementations the tool uses, along with the TigerVNC project for the community RFB specification document. The DES acknowledgement matters for a specific reason, since RFB has several pixel encodings and the older ones rely on DES-based authentication.

The README says the project is under active development and seems to be working, but please report any problems you have, which is a candid phrasing for a tool whose usefulness depends on the server on the other end.

Installing from PyPI and the address syntax that trips people up

You need a VNC server first. The README says most virtualization products include one, or you can use RealVNC, TightVNC, or clone your Desktop using x11vnc.

The install is a single command:

bash
pip install vncdotool

Then you send a message to the server with `vncdo`:

bash
vncdo -s vncserver type "hello world"

The `vncserver` argument needs to be in the format `address[:display|::port]`, and the README gives four examples that together explain the whole syntax:

bash
# connect to 192.168.1.1 on default port 5900
vncdo -s 192.168.1.1 type "hello world"

# connect to localhost on display :3 (port 5903)
vncdo -s localhost:3 type "hello world"

# connect to myvncserver.com on port 5902 (two colons needed)
vncdo -s myvncserver.com::5902 type "hello world"

The rule is that a single colon means a display number and the port is derived from it, while a double colon means an explicit port. The comment in the README says two colons needed for the port form, and the display form maps display 3 to port 5903. The example is a display number, not a port, and getting that wrong connects you to the wrong service.

There is also an IPv6 form, and it needs the square brackets:

bash
# connect via IPv6 to localhost on display :3 (port 5903)
vncdo -s '[::1]:3' type "hello IPv6"

Screenshots work the same way:

bash
vncdo -s vncserver capture screen.png

So the command shape is always the same: connect with `-s`, then give an action. Type is one action, capture is another, and the rest of the API follows the same pattern.

The server support matrix is the most useful thing in the README

The README does something unusual and genuinely useful: it grades server support by evidence rather than by optimism. It says vncdotool speaks standard RFB and works with far more servers than it tests, and then immediately adds that this says what there is evidence for, not what works.

There are four grades.

Supported means all basic functions are known to work and are regression tested on every pull request. That list is TigerVNC, x11vnc, wayvnc, LibVNCServer, QEMU's built-in server and Selenoid.

Supported on their own OS is a narrower grade for servers that need a Windows or macOS runner: UltraVNC on Windows, and Apple Screen Sharing on macOS for connect, auth and input. The reason those are only tested on the pull requests that touch code able to affect them is that they need a Windows or macOS runner, and there is an honest note that Screen Sharing's rendering is checked by hand rather than in CI, because a hosted macOS runner has no attached display, so its captures come back near-solid whatever the client does.

Compatible means a user reported it working, or the compatibility suite was run against it once. The README says these are not tracked by CI so they age, and that nothing is listed yet.

Broken is KasmVNC, and the detail is the most instructive part of the README. Connecting, key input and capture work, but any pointer event stops the server answering and it drops the connection twenty seconds later. The reason given is that KasmVNC states it has left the RFB specification and does not support VNC client applications, so no fix is expected. There is even a workaround flag, `vncdo --dialect kasmvnc`, which sends the eleven-byte pointer event that server reads instead of RFB's six, which keeps the session alive and moves the pointer, though a button press reaches the X server and has not been seen to reach an application.

TBD is RealVNC, TightVNC, TurboVNC, PiKVM and everything else, with the position stated plainly: it should work, we make no claim, a report either way is welcome and is how a server moves up.

That is a better contract than a list of logos, and it is worth reading before choosing a server.

How the project tests itself against real servers

The support matrix is not hand-waved, and the repository shows how the claims are produced.

The README says how the servers are tested is in `specs/testing-framework.md` and that the fleet itself is `tests/servers/docker-compose.yml`. So the supported servers run as containers, and the client is exercised against them.

The `Makefile` at the top of the repository lists the targets, and the list is the best description of the project's engineering practice available. There is `servers-up` to start the docker VNC test servers and `servers-down` to stop them, `test-servers` to smoke test the docker VNC test server fleet, and `test-os-server` to run functional tests against the operating system this machine's own VNC server, which is how the UltraVNC and Apple Screen Sharing entries are covered.

There are separate `test` and `test-func` targets for unit and functional tests, `typecheck` for mypy over the package, and `test-api` to run the in-process vncdotool.api lifecycle suite, which is the library-side test rather than the command-line one. `coverage` runs both suites under coverage and reports.

The visual side of testing is unusually well developed. There is a `screenshots` target that screenshots each running VNC test server into a gallery, a `goldens` target that captures decoder golden fixtures from the fleet, a `scenes` target that regenerates the committed scene PNGs from `tests/goldens/scenes.py`, and a `bench` target that times a decoder against a committed golden fixture, with `bench-record` appending a run to the tracked `bench.jsonl` and `bench-report` tabulating that file or diffing two runs' call counts.

So a decoder change can be checked against captured pixels from real servers, and a performance change can be measured and compared over time. That is a level of rigour that goes well beyond what a small protocol client usually has, and it is the mechanism behind the regression testing claim in the support matrix.

There is also a `prose-lint` target that checks the docs, README and specs with vale, which is why there is a `.vale.ini` in the tree.

The dependency set and what it says about the protocol support

The packaging metadata describes a project built on Twisted, and the dependency list is short enough to read in one go.

`Twisted[tls]` is the core, with a comment explaining that the tls extra is what `optionsForClientTLS` needs to verify a wss:// certificate, which tells you the tool supports connecting over a WebSocket transport with TLS. `cryptography>=43` provides the modern crypto primitives that replaced the older DES-era authentication, `Pillow` handles image encoding for screenshots, and `autobahn` is the WebSocket implementation.

The `Framework :: Twisted` classifier and the Twisted dependency together mean the network layer is event driven rather than thread per connection, which is the right shape for a client that may hold a long-lived session and also has to interleave with other work in a test harness.

The project requires Python 3.10 or later, and the classifiers name 3.10 through 3.13. The version in the packaging metadata is `2.0.0.dev0`, a development version, while the released versions in the tags are v1.4.2 from 2026-09-06, v1.4.1 and v1.4.0 both from 2026-08-19. So the main branch is working towards 2.0 while the published releases are still on the 1.4 line, which is a gap worth knowing about if you pin a version.

The development dependency group is also informative. It pins `coverage>=7.10` and adds `flake8` and `mypy`, along with `mypy-zope`, with a comment that it understands zope.interface, which Twisted is built on. Getting mypy to type check a Twisted codebase requires that plugin, and the comment is the kind of thing that tells you the type checking is real rather than nominal.

The `bench.jsonl` file at the top level and the `uv.lock` are the two other clues about how the project is run, and the `uv.toml` alongside them means the toolchain is uv rather than a bare virtualenv.

Where vncdotool is the wrong tool

The first wrong-tool case is anything that is not a VNC server. vncdotool speaks RFB. It does not speak the Windows management protocol, it does not drive a browser, and it does not have a library for a hypervisor's own API. If your virtualisation platform offers a control API, using that is a different and much better approach than automating a desktop inside the machine.

The second is a target with a scriptable interface. A GUI automation tool that types characters is a poor substitute for a configuration file, an API call or a command line flag, because it is slower, far more fragile, and it fails silently when the screen changes. Driving an installer by keystroke when the installer accepts an answer file is the wrong tool.

The third is visual verification of anything that renders remotely. The macOS entry in the support matrix contains the warning that matters most in this article: Screen Sharing's rendering is checked by hand rather than in CI, because a hosted macOS runner has no attached display, so its captures come back near-solid whatever the client does. In other words, on Apple Screen Sharing the capture path is known to be unreliable. Any test that asserts on pixel content from a macOS target should not rely on it.

The fourth is long-lived unattended use against an unverified server. The Compatible grade is explicitly not tracked by CI and ages, and the TBD list includes RealVNC and TightVNC, which are the two names most people meet first. The tool may well work with them, but the project makes no claim, and that is a different thing from a claim.

The alternative worth naming is the server's own API, for example QEMU's built-in control interface or Selenoid's HTTP API, and the difference in approach is that an API gives you state and actions directly, while a VNC client gives you a picture and a keyboard, which means you have to infer the state from the picture yourself. That inference is exactly what makes screen-driven tests flaky, and it is the reason the test suite in this repository invests in golden fixtures rather than comparing against a live screen every time.

Maintenance, packaging and the licence

The project is not archived, and the last push to the default branch was on 2026-10-10, with the most recent release v1.4.2 dated 2026-09-06 and v1.4.1 and v1.4.0 both dated 2026-08-19. The README describes it as under active development, and the density of the recent tags supports that.

The tree shows a well-organised project rather than a single-module script. There is a `vncdotool/` package directory, a `docs/` directory built with MkDocs, a `specs/` directory holding the testing framework document, a `tests/` directory with the server fleet, a `debian/` directory and an `rpm/` directory for system packaging, a `CHANGELOG.md`, a `TODO.txt`, a `DEVELOP.md` and a `bench.jsonl`.

The release process is scripted in the `Makefile`. The `release` target depends on `test-unit`, then bumps the version with `uv version --bump stable --no-sync`, rewrites the unreleased heading in `CHANGELOG.md` with the current date using `sd`, commits `pyproject.toml` and `CHANGELOG.md`, and tags and pushes to trigger the PyPI release. The `VERSION` and `NEXT_VERSION` variables are computed with `uv version --bump ... --dry-run --short --no-sync`, so the version numbers shown in the help output come from the same tool that performs the bump.

There are also distribution packages in `debian/` and `rpm/`, which is worth knowing if you are deploying to Linux servers where a pip install at boot is not acceptable.

On the licence, there is a small inconsistency to flag. The packaging metadata declares `license = "MIT"` with `license-files = ["LICENSE.txt"]` and a `LICENSE.txt` file in the tree, while the project listing records the licence as a custom one that GitHub cannot classify and points to the LICENSE file. If you need certainty about the terms, read `LICENSE.txt` in the repository. This is a note about where to look rather than legal advice.

The final practical point is about how to get help. The README points to the Stackoverflow tag for vncdotool, and asks that bug reports include the version of both vncdotool and the VNC server being used with it, which is the same information the support matrix is organised around.

Editorial conclusion

Adopt vncdotool when you need to drive a remote desktop as part of a test or a provisioning script, especially against the servers it tests against on every pull request, since TigerVNC, x11vnc, wayvnc, LibVNCServer, QEMU's built-in server and Selenoid are regression tested. Do not adopt it for real VNC applications that need to be supported long term, because the support matrix is honest about being thin, and do not use it to click through a graphical installer on a product that has a scriptable interface. Before you build on it, check your server against the support list, since KasmVNC is listed as broken and RealVNC, TightVNC and TurboVNC are unverified, and confirm the port and display syntax, because the address format differs between a display number and an explicit port.

Frequently asked questions

How do I install and use vncdotool to type on a remote VNC server?

Install it with `pip install vncdotool`, then run `vncdo -s vncserver type "hello world"`. The `vncserver` argument must be in the format `address[:display|::port]`, so `localhost:3` means display 3 on port 5903 while `myvncserver.com::5902` means port 5902, and an IPv6 address needs square brackets.

Which VNC servers does vncdotool support?

The README grades support by evidence. Supported and regression tested on every pull request are TigerVNC, x11vnc, wayvnc, LibVNCServer, QEMU's built-in server and Selenoid. UltraVNC on Windows and Apple Screen Sharing on macOS are supported on their own OS, KasmVNC is listed as broken, and RealVNC, TightVNC, TurboVNC and PiKVM are unverified.

Can vncdotool take a screenshot of the remote screen?

Yes, with the capture action: `vncdo -s vncserver capture screen.png`. The `Pillow` dependency in the package metadata is what handles the image encoding for this.

How is vncdotool tested against real VNC servers?

The servers run as containers, with the fleet in `tests/servers/docker-compose.yml` and the method described in `specs/testing-framework.md`. The `Makefile` provides `servers-up` and `servers-down`, a `test-servers` smoke test of the fleet, `test-os-server` for the host operating system's own server, golden fixtures captured from the fleet, and `bench` targets that time a decoder against a committed fixture.

Is vncdotool usable as a Python library as well as a command?

Yes. The repository is described as a command line VNC client and Python library, and the packaging declares four console scripts: `vncdo` and `vncdotool` for the main command, `vnclog`, and `vncdo-replay`. The Makefile also has a `test-api` target for the in-process `vncdotool.api` lifecycle suite.

Official sources

  1. Issues
  2. README
  3. Releases
  4. sibson/vncdotool 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/sibson-vncdotool.svg)](https://hysenlabs.com/projects/sibson-vncdotool)

Was this useful?