{"id":49831903,"url":"https://github.com/reactivecore/vnc-rdx","last_synced_at":"2026-05-13T21:03:03.199Z","repository":{"id":354867076,"uuid":"1225588371","full_name":"reactivecore/vnc-rdx","owner":"reactivecore","description":null,"archived":false,"fork":false,"pushed_at":"2026-05-07T16:11:15.000Z","size":8202,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-05-07T18:19:56.090Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/reactivecore.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-30T12:39:43.000Z","updated_at":"2026-05-07T16:19:13.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/reactivecore/vnc-rdx","commit_stats":null,"previous_names":["reactivecore/vnc-rdx"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/reactivecore/vnc-rdx","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reactivecore%2Fvnc-rdx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reactivecore%2Fvnc-rdx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reactivecore%2Fvnc-rdx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reactivecore%2Fvnc-rdx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/reactivecore","download_url":"https://codeload.github.com/reactivecore/vnc-rdx/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reactivecore%2Fvnc-rdx/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32999522,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-13T13:14:54.681Z","status":"ssl_error","status_checked_at":"2026-05-13T13:14:51.610Z","response_time":115,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-13T21:02:55.360Z","updated_at":"2026-05-13T21:03:03.193Z","avatar_url":"https://github.com/reactivecore.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# vncex\n\n\u003e **⚠ Beta — AI-assisted code, no warranty, unstable wire format**\n\u003e\n\u003e - This codebase was written with substantial AI assistance and has\n\u003e   **not been exhaustively human-reviewed**. Treat it accordingly.\n\u003e - Provided **as-is, without warranty of any kind** — see\n\u003e   [`LICENSE-MIT`](LICENSE-MIT) and [`LICENSE-APACHE`](LICENSE-APACHE).\n\u003e - The RDX wire protocol is **not stable** while in beta. Minor\n\u003e   version bumps may break compatibility. If you depend on this\n\u003e   crate, pin a specific git revision.\n\nA VNC/RFB proxy pair that tunnels sessions over a custom framed\nprotocol called **RDX** (Remote Desktop eXchange). Two thin binaries\nflank an RDX TCP link:\n\n```\nVNC viewer ── RFB ─▶ client ── RDX/TCP ─▶ server --source vnc ── RFB ─▶ VNC server\n```\n\nThe `server` binary can also capture a local Linux desktop directly,\nremoving the upstream VNC server from the path:\n\n```\nserver --source local ◀── (Wayland portal | X11 XTEST) ── local display\n       │\n       └── RDX/TCP ─▶ client ─▶ VNC viewer        (or any other RDX peer)\n```\n\nA browser viewer is also supported via the optional WebSocket\nendpoint on `server`; see \"Browser viewer\" below.\n\nThe **protocol is the first-class artifact**. The canonical wire-format\nspec lives in [`docs/protocol.md`](docs/protocol.md); this crate is its\nexecutable mirror. If the code disagrees with the spec, the code is\nwrong.\n\n## Crate layout\n\nDependencies flow **downward only** through these layers:\n\n| Layer | Purpose |\n|---|---|\n| `bin/` | Two thin binary entry points: `client` (viewer side) and `server` (display-source side). |\n| `client/` | Viewer-facing bridge: speaks RFB to a local VNC viewer and RDX to the peer `server`. Pure consumer — no encoders. |\n| `server/` | Display-source bridges, with two flavours: `server::upstream_vnc` proxies a remote VNC server; `server::local` captures from a local Wayland/X11 backend in `server::backend`. The optional `server::ws_listen` HTTP/WebSocket endpoint works with either source. |\n| `common/` | Shared library: `cache`, `change`, `session`, `rfb`, `transport`, `geom`, `config`, `error`, `metrics`, plus the bridge-side encoders (`framebuffer`, `tiler`, `video`, `auth`) and small `bridge_helpers`. Codec-agnostic; no binary-specific code. |\n| `proto/` | Pure codec: frame header, message bodies, handshake. No I/O, no async (except the `tokio_util::codec` traits). Kept usable from a future wasm/browser client. |\n\nFiles aim for **≤ 200 lines of non-test code**. When a module grows\npast that, it becomes a same-named directory with submodules organised\nby protocol section (see `proto/message/` for the canonical example).\n\n## The wire-codec contract\n\nEvery wire-format value implements one of two traits from\n`proto::wire`:\n\n```rust\n/// A self-contained wire value that owns its full byte slice.\n/// Implementors are message bodies from `docs/protocol.md` §4.\npub trait WireCodec: Sized {\n    fn encode(\u0026self, out: \u0026mut BytesMut);\n    fn decode(src: \u0026[u8]) -\u003e Result\u003cSelf\u003e;\n}\n\n/// A fixed-layout header that precedes a variable-length body.\n/// Decoding returns the tail so the caller can parse the body.\npub trait WireHeader: Sized {\n    fn encode(\u0026self, out: \u0026mut BytesMut);\n    fn decode(src: \u0026[u8]) -\u003e Result\u003c(Self, \u0026[u8])\u003e;\n}\n```\n\nUse `WireCodec` for message bodies (the common case). Use `WireHeader`\nfor fixed-layout preambles that front a variable payload, such as\n`TileDataHeader`, `TileDeltaHeader`, and `rfb::PixelFormat`.\n\nDo not re-invent `encode`/`decode` as inherent methods — if you are\nadding a wire type, implement one of these traits so the contract\ncan't drift.\n\n## Building and testing\n\n```sh\ncargo build                                # dev build (client + server stub backends)\ncargo build --release\ncargo build --no-default-features          # drop zstd (pure-Rust fallback: RAW + JPEG)\ncargo build --features websocket           # browser RDX endpoint (see below)\n\n# Native display backends for `server --source local` (Linux). Each\n# pulls in its own deps: x11rb for X11, ashpd + pipewire + wl-copy /\n# wl-paste for Wayland. Without these features the binary still\n# builds but `--source local` falls back to a stub backend that\n# refuses every connection.\ncargo build --features x11 --bin server\ncargo build --features wayland --bin server\ncargo build --features \"wayland x11\" --bin server\n\ncargo test                                 # full unit + integration suite\ncargo test --features websocket            # adds tests/websocket_bridge.rs\ncargo clippy --all-targets -- -D warnings  # must be clean before commit\ncargo fmt\ncargo doc --no-deps                        # check rustdoc coverage\n\n(cd web \u0026\u0026 npm ci \u0026\u0026 npm test)             # JS viewer unit + replay tests\n(cd web \u0026\u0026 npm run check)                  # tsc --noEmit --checkJs (JSDoc)\n```\n\n## End-to-end smoke test\n\n```sh\n# Terminal 1: server side, talking to a real VNC server.\nserver --source vnc --listen 127.0.0.1:7900 --connect \u003creal-vnc\u003e:5900\n\n# Terminal 2: client side, accepting a local VNC viewer.\nclient --listen 127.0.0.1:5901 --connect 127.0.0.1:7900\n```\n\nThen point a VNC viewer at `127.0.0.1:5901`.\n\n### Manual testing `server --source local`\n\n`--source local` replaces the upstream-VNC half of the smoke test\nwith a direct capture of the local desktop. Build with the\nappropriate backend feature, then:\n\n```sh\n# Terminal 1, on the host whose desktop you want to share.\n# Picks Wayland if WAYLAND_DISPLAY/XDG_SESSION_TYPE=wayland is set,\n# else X11 if $DISPLAY is set. Override with --backend wayland|x11.\ncargo run --features wayland --bin server -- --source local --listen 127.0.0.1:7900\n\n# Terminal 2: the same client bridge as above, accepting a VNC viewer.\nclient --listen 127.0.0.1:5901 --connect 127.0.0.1:7900\n```\n\nThen point a VNC viewer at `127.0.0.1:5901`. On Wayland the portal\nwill pop a permission prompt the first time it runs.\n\nBackend prerequisites:\n\n- **X11 (`--features x11`):** `XTEST`, `XKB`, and `RANDR` extensions —\n  every modern X server has these. Set `DISPLAY` (and `XAUTHORITY`\n  when running over SSH). See\n  [`docs/x11-backend.md`](docs/x11-backend.md) for the capture /\n  input / clipboard design and the comparison vs the Wayland path.\n- **Wayland (`--features wayland`):** an `xdg-desktop-portal`\n  implementation with the RemoteDesktop + ScreenCast portals (GNOME,\n  KDE, and most other desktop sessions ship one). The clipboard path\n  shells out to `wl-copy` / `wl-paste` from `wl-clipboard`; install\n  that package or expect clipboard sync to be a no-op. See\n  [`docs/wayland-backend.md`](docs/wayland-backend.md) for the\n  portal/PipeWire/GBM design and known upstream issues\n  (HiDPI fractional-scale pointer behaviour, keyboard-layout\n  dependence, and per-compositor caveats).\n\n#### Password authentication\n\n`server --source local` can require a password before letting a\nviewer reach the session. When configured, the server advertises\n`CAP_AUTH_FORWARDING` in the RDX HELLO and runs an RFB SecVNC\nchallenge through the existing AUTH_FRAME pipe before the session\nis initialised. The viewer side needs no flag — `client`\nauto-detects the cap bit and routes its local VNC viewer through\nthe auth pipe. (For `--source vnc`, the upstream VNC server's own\nauth applies and `--password` on the proxy is ignored.)\n\nThree knobs, in priority order (CLI \u003e env \u003e file):\n\n```sh\n# Highest priority: pass on the command line (visible in `ps`).\nserver --source local --listen 127.0.0.1:7900 --password hunter2\n\n# Or via environment.\nRDX_PASSWORD=hunter2 server --source local --listen 127.0.0.1:7900\n\n# Or read from a file (newline-trimmed).\nserver --source local --listen 127.0.0.1:7900 --password-file ~/.server.pw\n```\n\n**Caveat:** SecVNC is DES — fine for non-public networks; pair\nwith an SSH tunnel if confidentiality matters. RFB caps the\npassword at 8 bytes; longer passwords are silently truncated\n(matches every other VNC implementation).\n\n## Browser viewer (WebSocket endpoint)\n\nThe `server` binary optionally serves a binary WebSocket alongside its\nTCP RDX listener so a browser can speak RDX directly — no separate\n`client` process needed on the consumer side. The viewer is a pure-ESM\nJavaScript module under [`web/`](web/) and is embedded in the binary;\nopening `http://server:port/` shows the demo page. Works with both\n`--source vnc` and `--source local`.\n\nBuild with the feature flag and pass `--ws-listen`:\n\n```sh\ncargo build --release --features websocket\ntarget/release/server --source vnc \\\n    --listen 127.0.0.1:7900 \\\n    --ws-listen 127.0.0.1:8080 \\\n    --connect \u003creal-vnc\u003e:5900\n```\n\nNow navigate to `http://127.0.0.1:8080/` in any modern browser.\n\nThe viewer decodes every pixel codec the bridge emits (`RAW_BGRA`,\n`ZSTD_BGRA`, `JPEG_LOW`, `SOLID_COLOR`, `PALETTE_RAW`, `PALETTE_ZSTD`),\napplies `TILE_DELTA`, and handles both aligned and sub-tile `TILE_COPY`.\nIt also negotiates the §9.6 video transport when the browser supports\nit — a WebCodecs `VideoDecoder` (hardware-accelerated in practice;\nHEVC / AV1 / VP9, probed via `isConfigSupported`) — which is the path\nyou get on macOS, where the bridge's default `HardwareOnly` video\npolicy yields HEVC. The `/ws` endpoint rejects cross-origin upgrades;\nonly bind `--ws-listen` to a trusted interface (loopback for local\npreviews).\n\nOn a fatal client-side error (e.g. a `VideoDecoder` failure) the viewer\nships a one-shot diagnostic bundle to the bridge over the `STATS`\nchannel — so the cause shows up in the bridge log without the browser\nconsole. Pass `?diag=0` (or `sendDiagnostics: false` to `RdxClient`) to\ndisable the channel entirely.\n\nThe JS module type-checks via JSDoc + `// @ts-check` (no runtime\nTypeScript dependency); CI runs `node --test` and `tsc --noEmit\n--checkJs` against `web/`.\n\n## Contributor rules\n\nContributor rules, project-wide invariants, and the \"where state really\nlives\" map are in [`CLAUDE.md`](CLAUDE.md). Read that file before\ntouching the tile cache, stream reassembly, or the session state\nmachine — it documents non-obvious invariants that the type system\ndoes not enforce on its own.\n\n## Using as a library\n\n`vncex` ships both a binary (the proxy CLI) and a library. The crate\nis **not published to crates.io** during beta — depend on it as a git\ndependency and pin a revision so a wire-format change in `main` can't\nbreak your build:\n\n```toml\n[dependencies]\nvncex = { git = \"https://github.com/reactivecore/vnc-rdx\", rev = \"\u003ccommit-sha\u003e\" }\n```\n\nThe public surface is the set of modules re-exported from `lib.rs`:\n`client`, `server`, `proto`, `common` (which itself re-exports\n`cache`, `change`, `config`, `error`, `geom`, `metrics`, `rfb`,\n`session`, `transport`, `framebuffer`, `tiler`, `video`, `auth`),\nplus `Error` / `Result`. See `cargo doc --open` for the rendered API.\n\n## License\n\nDual-licensed under either of\n\n- **MIT License** ([`LICENSE-MIT`](LICENSE-MIT) or\n  https://opensource.org/licenses/MIT)\n- **Apache License, Version 2.0** ([`LICENSE-APACHE`](LICENSE-APACHE) or\n  https://www.apache.org/licenses/LICENSE-2.0)\n\nat your option. Copyright © 2026 Reactive Core GmbH.\n\nUnless you explicitly state otherwise, any contribution intentionally\nsubmitted for inclusion in this crate by you, as defined in the\nApache-2.0 license, shall be dual-licensed as above, without any\nadditional terms or conditions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freactivecore%2Fvnc-rdx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Freactivecore%2Fvnc-rdx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freactivecore%2Fvnc-rdx/lists"}