Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: Documentation

on:
push:
branches: [main]
paths:
- "docs/**"
- "mkdocs.yml"
- "requirements-docs.txt"
- ".github/workflows/docs.yml"
pull_request:
paths:
- "docs/**"
- "mkdocs.yml"
- "requirements-docs.txt"
- ".github/workflows/docs.yml"
workflow_dispatch:

permissions:
contents: read

concurrency:
group: pages-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
name: Build documentation
runs-on: ubuntu-latest
timeout-minutes: 10

steps:
- name: Check out repository
uses: actions/checkout@v7

- name: Set up Python
uses: actions/setup-python@v7
with:
python-version: "3.13"
cache: pip
cache-dependency-path: requirements-docs.txt

- name: Install documentation dependencies
run: python -m pip install -r requirements-docs.txt

- name: Build documentation
run: mkdocs build --strict --site-dir site

- name: Configure GitHub Pages
if: github.event_name != 'pull_request'
uses: actions/configure-pages@v5

- name: Upload GitHub Pages artifact
if: github.event_name != 'pull_request'
uses: actions/upload-pages-artifact@v4
with:
path: site

deploy:
name: Deploy GitHub Pages
if: github.event_name != 'pull_request'
needs: build
runs-on: ubuntu-latest
timeout-minutes: 10

permissions:
pages: write
id-token: write

environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}

steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
43 changes: 32 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ RFB 3.8 · UltraVNC interoperability · Tight / ZRLE / Hextile / Zlib · WebSock
<img alt="UltraVNC" src="https://img.shields.io/badge/UltraVNC-tested-2ea44f">
<img alt="WebSocket" src="https://img.shields.io/badge/WebSocket-noVNC-ff9800">
<img alt="Tests" src="https://img.shields.io/badge/tests-326%20passed-2ea44f">
<a href="https://xulek.github.io/PyVNCServer/"><img alt="Documentation" src="https://img.shields.io/badge/docs-GitHub%20Pages-0ea5e9?logo=materialformkdocs&logoColor=white"></a>
</p>

**PyVNCServer** is a Python implementation of a VNC/RFB server focused on protocol correctness, practical client interoperability, low-latency desktop streaming and a security-conscious default configuration.

[**Documentation**](https://xulek.github.io/PyVNCServer/) · [**GitHub**](https://github.com/xulek/PyVNCServer)

</div>

---
Expand All @@ -43,6 +46,8 @@ Highlights:
- Metrics, health checks and session instrumentation.
- Test suite covering protocol, encoders, WebSocket handling, security, capture and end-to-end RFB communication.

For the complete guides, configuration reference, architecture and troubleshooting documentation, see **https://xulek.github.io/PyVNCServer/**.

---

## Client compatibility
Expand All @@ -51,7 +56,7 @@ Highlights:
| --- | --- | --- |
| **UltraVNC Viewer** | ✅ Tested | Tight, RRE, Hextile, Zlib, ZRLE and Raw negotiation paths tested during 3.2.1 work |
| **Standard RFB 3.8 clients** | ✅ Supported | Client must advertise at least one encoding implemented by the server |
| **noVNC / browser clients** | ✅ Supported transport | Enable WebSocket and configure an origin allowlist; serve the noVNC frontend separately |
| **noVNC / browser clients** | ✅ Supported transport | noVNC is tracked as `web/noVNC`; enable WebSocket and configure an Origin allowlist |
| **Raw TCP VNC** | ✅ Supported | Default transport |
| **TLS-wrapped VNC** | ✅ Optional | Requires certificate and private key configuration |

Expand All @@ -70,13 +75,21 @@ Highlights:

### Install from source

Clone recursively to initialize the bundled noVNC submodule:

```bash
git clone <your-repository-url>
git clone --recurse-submodules https://github.com/xulek/PyVNCServer.git
cd PyVNCServer
python -m pip install -U pip
python -m pip install -e .
```

If you already cloned the repository without submodules:

```bash
git submodule update --init --recursive
```

For the faster optional capture stack:

```bash
Expand Down Expand Up @@ -156,6 +169,8 @@ tight_disable_for_ultravnc = false

Use these as troubleshooting switches rather than enabling them automatically for every client.

More details: [UltraVNC documentation](https://xulek.github.io/PyVNCServer/ultravnc/).

---

## noVNC / WebSocket mode
Expand All @@ -182,7 +197,9 @@ The WebSocket implementation accepts the binary transport used by noVNC and enfo
- bounded handshake size,
- preservation of bytes pipelined after the HTTP upgrade request.

The repository contains noVNC assets under `web/noVNC/`. PyVNCServer provides the VNC WebSocket transport; the static noVNC frontend should be served with your preferred HTTP server or reverse proxy.
noVNC is tracked as the `web/noVNC` Git submodule. PyVNCServer provides the VNC WebSocket transport; serve the noVNC frontend with your preferred HTTP server or reverse proxy.

More details: [noVNC & WebSocket documentation](https://xulek.github.io/PyVNCServer/novnc/).

---

Expand All @@ -202,6 +219,8 @@ The repository contains noVNC assets under `web/noVNC/`. PyVNCServer provides th

The server negotiates only encodings advertised by the client and can select different encodings for different update regions.

See the [encoding reference](https://xulek.github.io/PyVNCServer/encodings/) for protocol and fallback details.

---

## Capture pipeline
Expand Down Expand Up @@ -280,6 +299,8 @@ auth_backoff_max_seconds = 2.0

Additional protections include connection admission limits, handshake timeouts and WebSocket payload limits.

See the full [security guide](https://xulek.github.io/PyVNCServer/security/).

---

## Configuration
Expand Down Expand Up @@ -332,6 +353,8 @@ max_client_cut_text = 16777216

`network_profile_override = "auto"` allows the server to classify the connection instead of forcing LAN tuning for every client.

The complete option reference is available in the [configuration documentation](https://xulek.github.io/PyVNCServer/configuration/).

---

## Architecture
Expand Down Expand Up @@ -385,6 +408,8 @@ src/vnc_lib/ compatibility implementation layer

`vnc_lib` is retained for compatibility with existing imports while the public package surface is exposed under `pyvncserver`.

More detail: [architecture documentation](https://xulek.github.io/PyVNCServer/architecture/).

---

## Performance design
Expand Down Expand Up @@ -469,14 +494,12 @@ Capture benchmarks depend on the host desktop environment and should be interpre

## GitHub Actions

The repository ships with a complete CI/release setup:
The repository currently contains these workflow definitions:

| Workflow | Purpose |
| --- | --- |
| `CI` | Tests Python 3.11–3.13, includes a Windows test job, checks bytecode compilation and validates package builds |
| `CodeQL` | Python static security analysis on pushes, pull requests and a weekly schedule |
| `Release` | Validates the tag/version, builds wheel + sdist, creates a GitHub Release and optionally publishes to PyPI |
| `Benchmarks` | Manual encoder benchmark run with downloadable results |
| `CI` | Tests Python 3.11–3.13, includes a Windows test job, checks bytecode compilation, coverage and package builds |
| `Documentation` | Strictly builds MkDocs documentation on PRs and deploys GitHub Pages from `main` |

---

Expand Down Expand Up @@ -524,7 +547,6 @@ python -m twine check dist/*

---


## Known limitations / roadmap

- Native DXGI dirty/move rectangle harvesting is not implemented yet.
Expand All @@ -533,11 +555,10 @@ python -m twine check dist/*
- Browser use requires a separately served noVNC frontend/static HTTP endpoint.
- `vnc_lib` remains as a compatibility layer and can be progressively folded into the `pyvncserver` package structure.


---

<div align="center">

**PyVNCServer 3.2.1** · Python 3.11+ · RFB 3.8
**PyVNCServer 3.2.1** · Python 3.11+ · RFB 3.8 · [Documentation](https://xulek.github.io/PyVNCServer/)

</div>
73 changes: 73 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# CLI & Python API

## Command line

The installed entry point is:

```text
pyvncserver
```

### Start the server

```bash
pyvncserver serve
```

### Custom configuration

```bash
pyvncserver serve --config path/to/config.toml
```

### Log level override

```bash
pyvncserver serve --log-level DEBUG
```

Accepted log-level values are handled by the server logging configuration; typical values are `DEBUG`, `INFO`, `WARNING`, `ERROR` and `CRITICAL`.

Running without an explicit subcommand defaults to `serve`.

## Public Python package surface

The top-level package currently exports:

```python
from pyvncserver import (
__version__,
DEFAULT_CONFIG_PATH,
ServerSettings,
VNCServer,
VNCServerV3,
load_config_file,
)
```

### Load and validate configuration

```python
from pyvncserver import ServerSettings

settings = ServerSettings.from_file("config/pyvncserver.toml")
print(settings.host, settings.port)
```

`ServerSettings` is immutable (`frozen`) and validates core/security values before use.

### Flat compatibility mapping

```python
from pyvncserver import load_config_file

config = load_config_file("config/pyvncserver.toml")
print(config["host"])
print(config["websocket_max_message_bytes"])
```

The loader flattens TOML sections into the mapping expected by the existing runtime while preserving typed validation through `ServerSettings`.

## Stability note

`pyvncserver` is the public package surface. `vnc_lib` remains an internal/compatibility implementation layer; new integrations should prefer imports from `pyvncserver` where an equivalent public symbol exists.
70 changes: 70 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Architecture

PyVNCServer separates listener/session orchestration from reusable protocol, capture, runtime and encoding components while retaining `vnc_lib` as a compatibility implementation layer.

## Runtime flow

```mermaid
sequenceDiagram
participant C as VNC client
participant S as VNCServer
participant P as RFB protocol
participant CP as CaptureProducer
participant E as Encoding pipeline

C->>S: TCP / WebSocket connection
S->>P: RFB version + security negotiation
C->>P: ClientInit / SetPixelFormat / SetEncodings
C->>S: FramebufferUpdateRequest
CP-->>S: latest framebuffer generation
S->>E: changed regions + client capabilities
E-->>S: encoded rectangles
S-->>C: FramebufferUpdate
```

## Main package layout

```text
src/pyvncserver/
├── app/server.py listener, admission, authentication, lifecycle
├── session/ per-client state and message/update loop
├── platform/ capture producer and platform integration facade
├── runtime/ limits, registry, network/runtime helpers
├── rfb/ public RFB protocol facade
├── observability/ logging, metrics and profiling facade
├── config.py typed TOML loading and fail-closed validation
├── default_config.toml packaged safe defaults
└── cli.py pyvncserver command

src/vnc_lib/ protocol/encoder compatibility implementation layer
```

## Capture model

With `enable_capture_producer = true`, one server-wide producer captures the desktop and publishes monotonically newer framebuffer generations. Client sessions consume the newest appropriate generation instead of independently serializing capture calls.

Benefits:

- capture work does not scale linearly with client count;
- slow clients do not force every other client to capture at their pace;
- changed-region history can be associated with produced frames.

## Session state

Per-client state includes negotiated pixel format, client-advertised encodings, framebuffer request state, input-control state and encoder/session-specific state. Keeping this scoped to the connection prevents one viewer's negotiation from mutating another viewer's protocol state.

## Encoding pipeline

The framebuffer path can:

1. obtain the latest frame;
2. determine whether the request is full or incremental;
3. collect or detect changed regions;
4. split large/problematic regions where required;
5. choose only from negotiated encodings;
6. encode regions, optionally in parallel;
7. send one RFB FramebufferUpdate containing consistent rectangle headers/payloads.

## Compatibility layer

`src/vnc_lib/` remains in the repository because existing implementation and import paths still depend on it. Public package exports are provided through `pyvncserver`; future refactors can move implementation behind those public modules without forcing downstream callers to follow every internal reorganization.
13 changes: 13 additions & 0 deletions docs/assets/images/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading