mirror of
https://github.com/CloudNebulaProject/wayray.git
synced 2026-04-10 05:00:40 +00:00
Add project documentation, architecture decisions, and usage book
Comprehensive documentation for WayRay, a SunRay-like thin client Wayland compositor targeting illumos and Linux: - CLAUDE.md: project context and conventions - docs/ai/plans: 6-phase implementation roadmap - docs/ai/adr: 9 architecture decision records (Smithay, QUIC, frame encoding, session management, rendering, audio, project structure, illumos support, pluggable window management) - docs/architecture: system architecture overview with diagrams - docs/protocols: WayRay wire protocol specification - book/: mdbook user guide (introduction, concepts, server/client guides, admin, development) - RESEARCH.md: deep research on remote display protocols
This commit is contained in:
parent
a82d60b835
commit
167c6c17c6
40 changed files with 3556 additions and 0 deletions
60
CLAUDE.md
Normal file
60
CLAUDE.md
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# WayRay - SunRay-like Thin Client Wayland Compositor
|
||||
|
||||
## Project Overview
|
||||
|
||||
WayRay is a modern reimplementation of Oracle/Sun's SunRay thin client architecture as a Wayland compositor written in Rust. The goal is to provide stateless thin client computing with session mobility, hot-desking, and zero-state endpoints over a modern protocol stack.
|
||||
|
||||
## Target Platforms
|
||||
|
||||
- **illumos** (OpenIndiana, OmniOS, SmartOS) - Primary target, honoring SunRay's Solaris heritage
|
||||
- **Linux** - Full support with optional GPU acceleration
|
||||
- Architecture is **headless-first**: Smithay with `default-features = false`, no dependency on DRM/KMS, libinput, udev, or libseat in the core path. Platform-specific backends behind feature flags.
|
||||
|
||||
## Language & Stack
|
||||
|
||||
- **Language**: Rust (edition 2024)
|
||||
- **Compositor Framework**: Smithay (Wayland compositor library)
|
||||
- **Event Loop**: calloop (callback-based, not async)
|
||||
- **Networking**: QUIC via quinn for low-latency multiplexed transport
|
||||
- **Encoding**: H.264/AV1 via VAAPI/NVENC for display, Opus for audio
|
||||
- **Error Reporting**: miette with diagnostic patterns for user-facing errors
|
||||
- **ORM**: SeaORM for any database needs (never raw SQL)
|
||||
- **Logging**: tracing crate
|
||||
|
||||
## Architecture
|
||||
|
||||
WayRay consists of four main components:
|
||||
|
||||
1. **wayray-server** (compositor) - Smithay-based Wayland compositor that runs applications, captures framebuffers, and transmits them to clients. Exposes a pluggable WM protocol.
|
||||
2. **wayray-client** (viewer) - Lightweight display client that decodes frames, renders them locally, captures input, and sends it to the server
|
||||
3. **wayray-protocol** - Shared protocol definitions for the WayRay wire protocol over QUIC
|
||||
4. **wayray-ctl** - CLI management tool for server/session administration
|
||||
|
||||
## Documentation
|
||||
|
||||
- **docs/ai/plans/** - Implementation plans
|
||||
- **docs/ai/adr/** - Architecture Decision Records
|
||||
- **docs/architecture/** - System architecture documentation
|
||||
- **docs/protocols/** - Protocol specifications
|
||||
- **book/** - mdbook user guide (build with `mdbook build book/`)
|
||||
|
||||
## Key Design Decisions
|
||||
|
||||
- **Headless-first**: No hard dependency on GPU, DRM, or Linux-specific subsystems
|
||||
- **illumos + Linux**: Portable core, platform-specific backends behind feature flags
|
||||
- **Pluggable Window Management**: External WM process via custom Wayland protocol (River-inspired two-phase transaction model). Ships with built-in floating WM as default.
|
||||
- QUIC over TCP for network transport (multiplexing, 0-RTT reconnect)
|
||||
- PixmanRenderer for headless server (no GPU needed), GlesRenderer when GPU available
|
||||
- Damage-tracked differential frame encoding
|
||||
- Smart card / token-based session mobility (hot-desking)
|
||||
- Userspace USB forwarding over QUIC (not kernel USB/IP, for portability)
|
||||
- Audio: PipeWire (Linux) or PulseAudio (illumos/Linux) behind trait abstraction
|
||||
- `shm_open` fallback when `memfd_create` unavailable (illumos portability)
|
||||
|
||||
## Conventions
|
||||
|
||||
- Use miette's diagnostic pattern for all user-facing errors with helpful messages
|
||||
- Never use raw SQL - use SeaORM entities
|
||||
- Docker builds must use current Rust version with target-platform-specific caches
|
||||
- Clean up unused variables but investigate if design changes caused them to become unused
|
||||
- Use context7 for library documentation lookups
|
||||
994
RESEARCH.md
Normal file
994
RESEARCH.md
Normal file
|
|
@ -0,0 +1,994 @@
|
|||
# Remote Display & Thin Client Technologies Research
|
||||
|
||||
Comprehensive research for building a SunRay-like thin client system. Covers protocols, capture mechanisms, encoding, networking, and audio/USB forwarding.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [SPICE Protocol](#1-spice-protocol)
|
||||
2. [RDP (Remote Desktop Protocol)](#2-rdp-remote-desktop-protocol)
|
||||
3. [VNC / RFB Protocol](#3-vnc--rfb-protocol)
|
||||
4. [Waypipe](#4-waypipe)
|
||||
5. [PipeWire Screen Capture](#5-pipewire-screen-capture)
|
||||
6. [Video Codecs for Remote Display](#6-video-codecs-for-remote-display)
|
||||
7. [Network Protocols](#7-network-protocols)
|
||||
8. [Framebuffer Capture Techniques](#8-framebuffer-capture-techniques)
|
||||
9. [Audio Forwarding](#9-audio-forwarding)
|
||||
10. [USB/IP](#10-usbip)
|
||||
11. [Modern Thin Client Projects](#11-modern-thin-client-projects)
|
||||
12. [Architecture Recommendations](#12-architecture-recommendations-for-a-sunray-like-system)
|
||||
|
||||
---
|
||||
|
||||
## 1. SPICE Protocol
|
||||
|
||||
**SPICE** (Simple Protocol for Independent Computing Environments) is a remote display protocol originally developed by Qumranet (acquired by Red Hat). It is the most architecturally relevant existing protocol for a SunRay-like system.
|
||||
|
||||
### Architecture
|
||||
|
||||
SPICE has a four-component architecture:
|
||||
- **Protocol**: Wire format specification for all messages
|
||||
- **Server** (`libspice-server`): Runs inside the hypervisor/host, directly accesses the virtual GPU framebuffer
|
||||
- **Client** (`spice-gtk`, `remote-viewer`): Renders display, captures input, handles USB/audio
|
||||
- **Guest Agent** (`spice-vdagent`): Runs inside the guest VM for clipboard, resolution changes, file transfer
|
||||
|
||||
### Channel Architecture
|
||||
|
||||
Each SPICE session consists of **multiple independent TCP/TLS connections**, one per channel type:
|
||||
|
||||
| Channel | ID | Purpose |
|
||||
|---|---|---|
|
||||
| **Main** | 1 | Session management, migration, agent communication |
|
||||
| **Display** | 2 | Rendering commands, images, video streams |
|
||||
| **Inputs** | 3 | Keyboard and mouse events |
|
||||
| **Cursor** | 4 | Pointer shape and position |
|
||||
| **Playback** | 5 | Audio output (server -> client) |
|
||||
| **Record** | 6 | Audio input (client -> server) |
|
||||
| **Smartcard** | 8 | Smartcard passthrough |
|
||||
| **USB Redir** | 9 | USB device forwarding via usbredir |
|
||||
| **Port** | 10 | Generic data port |
|
||||
| **Webdav** | 11 | File sharing via WebDAV |
|
||||
|
||||
### Display Channel & Image Compression
|
||||
|
||||
The display channel is the most complex. SPICE does **not** just send raw framebuffer pixels. Instead it sends **rendering commands** (draw operations, images, etc.) and tries to **offload rendering to the client GPU**.
|
||||
|
||||
**Image compression algorithms** (selectable at runtime):
|
||||
- **Quic**: Proprietary algorithm based on SFALIC. Optimized for photographic/natural images
|
||||
- **LZ**: Standard Lempel-Ziv. Good for text/UI content
|
||||
- **GLZ** (Global LZ): LZ with a **history-based global dictionary** that exploits repeating patterns across images. Critical for WAN performance
|
||||
- **Auto mode**: Heuristically selects Quic vs. LZ/GLZ per-image based on content type
|
||||
|
||||
**Video streaming**: The server **heuristically detects video regions** (rapidly changing rectangular areas) and encodes them as **M-JPEG streams**, dramatically reducing bandwidth for video playback.
|
||||
|
||||
**Caching**: Images, palettes, and cursor data are cached on the client side to avoid retransmission.
|
||||
|
||||
### Key Design Insights for WayRay
|
||||
|
||||
- The multi-channel approach allows independent QoS per data type
|
||||
- Sending rendering commands rather than raw pixels is more bandwidth-efficient
|
||||
- The automatic image compression selection based on content type is clever
|
||||
- GLZ's global dictionary approach is excellent for WAN scenarios
|
||||
- Video region detection and switching to video codec is a critical optimization
|
||||
|
||||
### Sources
|
||||
- [SPICE Protocol Specification](https://www.spice-space.org/spice-protocol.html)
|
||||
- [SPICE for Newbies](https://www.spice-space.org/spice-for-newbies.html)
|
||||
- [SPICE Features](https://www.spice-space.org/features.html)
|
||||
- [SPICE User Manual](https://www.spice-space.org/spice-user-manual.html)
|
||||
- [SPICE Protocol PDF](https://www.spice-space.org/static/docs/spice_protocol.pdf)
|
||||
- [SPICE Wikipedia](https://en.wikipedia.org/wiki/Simple_Protocol_for_Independent_Computing_Environments)
|
||||
|
||||
---
|
||||
|
||||
## 2. RDP (Remote Desktop Protocol)
|
||||
|
||||
**RDP** is Microsoft's proprietary remote desktop protocol, based on the ITU T.120 family of protocols. Default port: TCP/UDP 3389.
|
||||
|
||||
### Architecture
|
||||
|
||||
RDP uses a **client-server model** with a layered architecture:
|
||||
|
||||
1. **Transport Layer**: TCP/IP (traditional) or UDP (for lossy/real-time data)
|
||||
2. **Security Layer**: TLS/NLA (Network Level Authentication)
|
||||
3. **Core Protocol**: PDU (Protocol Data Unit) processing, state machine
|
||||
4. **Virtual Channel System**: Extensible channel framework for features
|
||||
|
||||
**Server-side components**:
|
||||
- `Wdtshare.sys`: RDP driver handling UI transfer, compression, encryption, framing
|
||||
- `Tdtcp.sys`: Transport driver packaging the protocol onto TCP/IP
|
||||
|
||||
### Virtual Channel System
|
||||
|
||||
RDP's extensibility comes from its virtual channel architecture:
|
||||
|
||||
**Static Virtual Channels (SVC)**:
|
||||
- Negotiated during connection setup
|
||||
- Fixed for session lifetime
|
||||
- Name limited to 8 bytes
|
||||
- Examples: `RDPSND` (audio), `CLIPRDR` (clipboard), `RDPDR` (device redirection)
|
||||
|
||||
**Dynamic Virtual Channels (DVC)**:
|
||||
- Built on top of the `DRDYNVC` static channel
|
||||
- Can be opened/closed during a session
|
||||
- Used for modern features: graphics pipeline, USB redirection, diagnostics
|
||||
- Microsoft's recommended approach for new development
|
||||
|
||||
### Graphics Pipeline
|
||||
|
||||
RDP has evolved through several graphics approaches:
|
||||
|
||||
1. **GDI Remoting** (original): Send Windows GDI drawing commands
|
||||
2. **RemoteFX Codec**: Wavelet-based (DWT + RLGR encoding), supports lossless and lossy modes
|
||||
3. **RemoteFX Progressive Codec**: Progressive rendering for WAN - sends low quality first, refines incrementally
|
||||
4. **GFX Pipeline** (`MS-RDPEGFX`): Modern graphics extension supporting:
|
||||
- AVC/H.264 encoding for video content
|
||||
- RemoteFX for non-video content
|
||||
- Adaptive selection based on content type and bandwidth
|
||||
|
||||
**Note**: RemoteFX vGPU was deprecated in 2020 due to security vulnerabilities; the codec itself lives on in the GFX pipeline.
|
||||
|
||||
### FreeRDP
|
||||
|
||||
[FreeRDP](https://github.com/FreeRDP/FreeRDP) is the dominant open-source RDP implementation (Apache 2.0 license):
|
||||
- Written primarily in C (87.8%)
|
||||
- Clean separation: `libfreerdp` (protocol) vs. client frontends vs. server implementations
|
||||
- Powers Remmina, GNOME Connections, KRDC, and most Linux RDP clients
|
||||
- Implements the full virtual channel system including GFX pipeline
|
||||
|
||||
### Key Design Insights for WayRay
|
||||
|
||||
- The SVC/DVC split is instructive: start with fixed channels, add dynamic ones later
|
||||
- Progressive rendering is excellent for variable-bandwidth scenarios
|
||||
- Content-adaptive encoding (H.264 for video, wavelet for desktop) is the modern approach
|
||||
- FreeRDP's architecture (protocol library separate from client/server) is a good model
|
||||
|
||||
### Sources
|
||||
- [Understanding RDP - Microsoft Learn](https://learn.microsoft.com/en-us/troubleshoot/windows-server/remote/understanding-remote-desktop-protocol)
|
||||
- [RDP Wikipedia](https://en.wikipedia.org/wiki/Remote_Desktop_Protocol)
|
||||
- [FreeRDP GitHub](https://github.com/FreeRDP/FreeRDP)
|
||||
- [MS-RDPEGFX Specification](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpegfx/da5c75f9-cd99-450c-98c4-014a496942b0)
|
||||
- [Graphics Encoding over RDP - Azure](https://learn.microsoft.com/en-us/azure/virtual-desktop/graphics-encoding)
|
||||
- [RDP Virtual Channels - Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/termserv/terminal-services-virtual-channels)
|
||||
|
||||
---
|
||||
|
||||
## 3. VNC / RFB Protocol
|
||||
|
||||
**VNC** (Virtual Network Computing) uses the **RFB** (Remote Framebuffer) protocol, standardized in [RFC 6143](https://www.rfc-editor.org/rfc/rfc6143.html).
|
||||
|
||||
### Architecture
|
||||
|
||||
RFB is a **simple, stateless framebuffer protocol**. The fundamental design:
|
||||
|
||||
- The display side is based on a single primitive: **"put a rectangle of pixel data at position (x, y)"**
|
||||
- A sequence of rectangles makes a **framebuffer update**
|
||||
- The protocol is **client-pull**: the client requests updates, the server sends them
|
||||
- **Pixel format** is negotiated: 24-bit true color, 16-bit, or 8-bit color-mapped
|
||||
|
||||
### Encoding Types
|
||||
|
||||
The encoding system is the key to VNC performance. Different encodings trade off bandwidth, client CPU, and server CPU:
|
||||
|
||||
| Encoding | Description | Best For |
|
||||
|---|---|---|
|
||||
| **Raw** | Uncompressed pixel data, scanline order | Fast LAN, low CPU |
|
||||
| **CopyRect** | Reference to existing framebuffer region | Window moves, scrolling |
|
||||
| **RRE** | Rise-and-Run-length Encoding, rectangles of solid color | Simple UIs |
|
||||
| **Hextile** | 16x16 tile subdivision with RRE within tiles | Fast LAN (low CPU overhead) |
|
||||
| **Zlib** | Raw data compressed with zlib | Moderate bandwidth savings |
|
||||
| **Tight** | Intelligent per-rectangle compression selection (zlib, JPEG, indexed color, solid) | Low bandwidth / WAN |
|
||||
| **ZRLE** | Zlib Run-Length Encoding, combines zlib with palette/RLE | Good all-around |
|
||||
| **TurboVNC/Tight+JPEG** | Tight with aggressive JPEG for photographic regions | Video content, high FPS |
|
||||
|
||||
**Pseudo-encodings** allow clients to advertise extension support (cursor shape, desktop resize, etc.) without changing the core protocol.
|
||||
|
||||
### Performance Characteristics
|
||||
|
||||
- **Fast LAN**: Hextile or Raw (minimize CPU overhead)
|
||||
- **WAN/Low bandwidth**: Tight (best compression ratios, especially for mixed content)
|
||||
- **Photo/Video content**: Tight with JPEG (TurboVNC achieves 4x better performance than ZRLE for images)
|
||||
- **Scrolling/Window moves**: CopyRect (near-zero bandwidth)
|
||||
|
||||
### Key Design Insights for WayRay
|
||||
|
||||
- CopyRect-style "reference previous frame data" is extremely efficient for common desktop operations
|
||||
- Per-rectangle encoding selection (as in Tight) is superior to one-size-fits-all
|
||||
- RFB's simplicity is both its strength (easy to implement) and weakness (no audio, USB, etc.)
|
||||
- The client-pull model introduces latency; a push model with damage tracking is better
|
||||
|
||||
### Sources
|
||||
- [RFC 6143 - The Remote Framebuffer Protocol](https://www.rfc-editor.org/rfc/rfc6143.html)
|
||||
- [RFB Protocol Documentation](https://vncdotool.readthedocs.io/en/0.8.0/rfbproto.html)
|
||||
- [RFB Protocol Wikipedia](https://en.wikipedia.org/wiki/RFB_protocol)
|
||||
- [VNC Tight Encoder Comparison](https://www.tightvnc.com/archive/compare.html)
|
||||
- [TigerVNC RFB Protocol](https://github.com/svn2github/tigervnc/blob/master/rfbproto/rfbproto.rst)
|
||||
|
||||
---
|
||||
|
||||
## 4. Waypipe
|
||||
|
||||
**Waypipe** is a proxy for Wayland clients, analogous to `ssh -X` for X11. It is the most directly relevant existing project for Wayland remote display.
|
||||
|
||||
### Architecture
|
||||
|
||||
Waypipe operates as a **paired proxy** system:
|
||||
|
||||
```
|
||||
[Remote App] <--Wayland--> [waypipe server] <--socket/SSH--> [waypipe client] <--Wayland--> [Local Compositor]
|
||||
```
|
||||
|
||||
- **Server mode**: Acts as a Wayland compositor stub on the remote side. Wayland apps connect to it as if it were a real compositor.
|
||||
- **Client mode**: Connects to the local real compositor and forwards surface updates from the remote side.
|
||||
- **SSH integration**: `waypipe ssh user@host app` sets up the tunnel automatically.
|
||||
|
||||
### Buffer Synchronization
|
||||
|
||||
This is the key technical innovation:
|
||||
|
||||
1. Waypipe keeps a **mirror copy** of each shared memory buffer
|
||||
2. When a buffer is committed, waypipe **diffs** the current buffer against the mirror
|
||||
3. Only **changed regions** are transmitted
|
||||
4. The remote side applies the diff to reconstruct the buffer
|
||||
|
||||
### Compression Options
|
||||
|
||||
| Method | Use Case | Default |
|
||||
|---|---|---|
|
||||
| **none** | High-bandwidth LAN | No |
|
||||
| **lz4** | General purpose, fast | Yes (default) |
|
||||
| **zstd** | Low-bandwidth / WAN | No |
|
||||
|
||||
Compression ratios: 30x for text-heavy content, down to 1.5x for noisy images.
|
||||
|
||||
### Video Encoding (DMA-BUF)
|
||||
|
||||
For DMA-BUF buffers (GPU-rendered content), waypipe supports **lossy video encoding**:
|
||||
|
||||
- `--video=sw,bpf=120000,h264` (default when `--video` is used)
|
||||
- **Software encoding** (libx264) or **hardware encoding** (VAAPI)
|
||||
- With VAAPI on Intel Gen8 iGPU: **80 FPS at 4 MB/s bandwidth**
|
||||
- Configurable bits-per-frame for quality/bandwidth tradeoff
|
||||
|
||||
### Protocol Handling
|
||||
|
||||
Waypipe parses the Wayland wire protocol, which is **partially self-describing**. It:
|
||||
- Intercepts buffer-related messages (wl_shm, wl_buffer, linux-dmabuf)
|
||||
- Passes through other messages transparently
|
||||
- Is partially forward-compatible with new Wayland protocols
|
||||
|
||||
### Limitations
|
||||
|
||||
- Per-application, not whole-desktop
|
||||
- No built-in audio forwarding
|
||||
- No USB forwarding
|
||||
- Performance depends heavily on application rendering patterns
|
||||
- Latency can be noticeable for interactive use
|
||||
|
||||
### Key Design Insights for WayRay
|
||||
|
||||
- The diff-based buffer synchronization is very efficient for incremental updates
|
||||
- VAAPI video encoding for DMA-BUF is the right approach for GPU-rendered content
|
||||
- Per-application forwarding is limiting; a whole-compositor approach is better for a thin client
|
||||
- The Wayland protocol's design (buffer passing, damage tracking) is well-suited for remote display
|
||||
|
||||
### Sources
|
||||
- [Waypipe GitHub](https://github.com/neonkore/waypipe)
|
||||
- [Waypipe Man Page](https://man.archlinux.org/man/extra/waypipe/waypipe.1.en)
|
||||
- [GSOC 2019 - Waypipe Development Blog](https://mstoeckl.com/notes/gsoc/blog.html)
|
||||
- [Waypipe DeepWiki](https://deepwiki.com/neonkore/waypipe/2-getting-started)
|
||||
|
||||
---
|
||||
|
||||
## 5. PipeWire Screen Capture
|
||||
|
||||
PipeWire is the modern Linux multimedia framework that unifies audio, video, and screen capture.
|
||||
|
||||
### Portal-Based Screen Capture Architecture
|
||||
|
||||
On Wayland, screen capture follows a **security-first architecture**:
|
||||
|
||||
```
|
||||
[Application] --> [xdg-desktop-portal (D-Bus)] --> [Portal Backend (compositor-specific)]
|
||||
|
|
||||
[PipeWire Stream]
|
||||
|
|
||||
[Application receives frames]
|
||||
```
|
||||
|
||||
**Flow**:
|
||||
1. Application calls `org.freedesktop.portal.ScreenCast.CreateSession()` via D-Bus
|
||||
2. Portal presents a permission dialog to the user
|
||||
3. On approval, `SelectSources()` lets user choose output/window
|
||||
4. `Start()` creates a PipeWire stream and returns a `pipewire_fd`
|
||||
5. Application connects to PipeWire using this fd and receives frames
|
||||
|
||||
### Buffer Sharing Mechanisms
|
||||
|
||||
PipeWire supports two buffer types for screen capture:
|
||||
|
||||
**DMA-BUF (preferred)**:
|
||||
- Zero-copy transfer from compositor GPU memory to consumer
|
||||
- Buffer stays in GPU VRAM throughout the pipeline
|
||||
- Ideal for hardware video encoding (capture -> encode without CPU copy)
|
||||
- Format/modifier negotiation ensures compatibility
|
||||
|
||||
**memfd (fallback)**:
|
||||
- Shared memory file descriptor
|
||||
- Requires CPU copy from GPU to system memory
|
||||
- Universal compatibility but higher overhead
|
||||
|
||||
### Wayland Capture Protocols
|
||||
|
||||
Three generations of capture protocols exist:
|
||||
|
||||
1. **wlr-export-dmabuf-unstable-v1** (legacy): Exports entire output as DMA-BUF frames. Simple but no damage tracking.
|
||||
|
||||
2. **wlr-screencopy-unstable-v1** (deprecated): More flexible, supports shared memory and DMA-BUF. Has damage tracking via `copy_with_damage`. Being replaced.
|
||||
|
||||
3. **ext-image-copy-capture-v1** (current, merged 2024): The new standard protocol:
|
||||
- Client specifies which buffer regions need updating
|
||||
- Compositor only fills changed regions
|
||||
- Supports both output capture and window capture
|
||||
- Initial implementations: wlroots, WayVNC, grim
|
||||
|
||||
### GNOME's Approach
|
||||
|
||||
GNOME/Mutter uses different D-Bus APIs:
|
||||
- `org.gnome.Mutter.ScreenCast`: Provides PipeWire stream of screen content
|
||||
- `org.gnome.Mutter.RemoteDesktop`: Provides input injection
|
||||
- These power `gnome-remote-desktop` which speaks RDP (and VNC)
|
||||
|
||||
### Key Design Insights for WayRay
|
||||
|
||||
- **ext-image-copy-capture-v1 + PipeWire** is the correct modern capture stack
|
||||
- DMA-BUF capture -> hardware encode is the zero-copy golden path
|
||||
- The portal system provides proper security/permission handling
|
||||
- For a thin client server running its own compositor, you can skip the portal and use the capture protocols directly
|
||||
- Damage tracking in ext-image-copy-capture-v1 is essential for efficient updates
|
||||
|
||||
### Sources
|
||||
- [XDG Desktop Portal ScreenCast API](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.ScreenCast.html)
|
||||
- [ext-image-copy-capture-v1 Protocol](https://wayland.app/protocols/ext-image-copy-capture-v1)
|
||||
- [wlr-screencopy-unstable-v1](https://wayland.app/protocols/wlr-screencopy-unstable-v1)
|
||||
- [wlr-export-dmabuf-unstable-v1](https://wayland.app/protocols/wlr-export-dmabuf-unstable-v1)
|
||||
- [Wayland Merges New Screen Capture Protocols - Phoronix](https://www.phoronix.com/news/Wayland-Merges-Screen-Capture)
|
||||
- [PipeWire ArchWiki](https://wiki.archlinux.org/title/PipeWire)
|
||||
- [Niri Screencasting Implementation](https://deepwiki.com/niri-wm/niri/5.4-screencasting-and-screen-capture)
|
||||
|
||||
---
|
||||
|
||||
## 6. Video Codecs for Remote Display
|
||||
|
||||
### Codec Comparison for Low-Latency Use
|
||||
|
||||
| Property | H.264/AVC | H.265/HEVC | AV1 |
|
||||
|---|---|---|---|
|
||||
| **Compression efficiency** | Baseline | ~35% better than H.264 | ~50% better than H.264 |
|
||||
| **Encoding latency** | Lowest | Low | Moderate (improving) |
|
||||
| **Hardware encode support** | Universal | Widespread | Newer GPUs only |
|
||||
| **Patent/license** | Licensed (but ubiquitous) | Licensed (complex) | Royalty-free |
|
||||
| **Screen content coding** | Limited | Better | Best (dedicated tools) |
|
||||
| **Decode support** | Universal | Nearly universal | Growing rapidly |
|
||||
| **Best for** | Maximum compatibility | Good quality/bandwidth | Best quality, royalty-free |
|
||||
|
||||
### Low-Latency Encoding Considerations
|
||||
|
||||
For remote desktop, encoding latency is critical. Key settings:
|
||||
|
||||
**Frame structure**:
|
||||
- **No B-frames**: B-frames require future frames, adding latency
|
||||
- **No lookahead**: Lookahead improves quality but adds latency
|
||||
- **No frame reordering**: Frames must be encoded/decoded in order
|
||||
- **Single slice / low-delay profile**: Minimizes buffering
|
||||
|
||||
**Rate control**:
|
||||
- **CBR (Constant Bit Rate)**: Keeps network queues short and predictable
|
||||
- **VBR with max bitrate cap**: Better quality but can cause bandwidth spikes
|
||||
- CBR is generally preferred for remote desktop due to predictable latency
|
||||
|
||||
**Intra refresh**:
|
||||
- Periodic I-frames are large and cause bandwidth spikes
|
||||
- **Gradual Intra Refresh (GIR)**: Spreads intra-coded blocks across frames, avoiding spikes
|
||||
- Essential for smooth, low-latency streaming
|
||||
|
||||
### AV1 Specific Advantages
|
||||
|
||||
AV1 has features specifically useful for remote desktop:
|
||||
- **Screen Content Coding (SCC)**: Dedicated tools for text, UI elements, and screen captures that dramatically reduce bitrate
|
||||
- **Temporal Scalability (SVC)**: L1T2 mode (1 spatial layer, 2 temporal layers) allows dropping frames gracefully under bandwidth pressure
|
||||
- **Film Grain Synthesis**: Can transmit film grain parameters instead of actual grain, saving bandwidth
|
||||
|
||||
Chrome's libaom AV1 encoder (speed 10): 12% better quality than VP9 at same bandwidth, 25% faster encoding.
|
||||
|
||||
### Hardware Encoding
|
||||
|
||||
#### NVIDIA NVENC
|
||||
|
||||
- Available on GeForce GTX 600+ and all Quadro/Tesla with Kepler+
|
||||
- **Video Codec SDK v13.0** (2025): AV1 ultra-high quality mode, comparable to software AV1 encoding
|
||||
- Latency modes:
|
||||
- **Normal Latency**: Default, uses B-frames and lookahead
|
||||
- **Low Latency**: No B-frames, no reordering
|
||||
- **Ultra Low Latency**: Strict in-order pipeline, minimal frame queuing
|
||||
- Dedicated hardware encoder block (does not consume CUDA cores)
|
||||
- Can encode 4K@120fps with sub-frame latency
|
||||
|
||||
#### Intel VAAPI (Video Acceleration API)
|
||||
|
||||
- Open-source API (`libva`) supported on Intel Gen8+ (Broadwell+)
|
||||
- Supports H.264, H.265, AV1 (Intel Arc/Gen12+), VP9
|
||||
- FFmpeg integration: `h264_vaapi`, `hevc_vaapi`, `av1_vaapi`
|
||||
- Low-power encoding mode available on some platforms
|
||||
- GStreamer integration via `gstreamer-vaapi`
|
||||
- Well-suited for always-on server scenarios (low power consumption)
|
||||
|
||||
#### AMD AMF/VCN
|
||||
|
||||
- Video Core Next (VCN) hardware encoder
|
||||
- Supports H.264, H.265, AV1 (RDNA 3+)
|
||||
- AMF (Advanced Media Framework) SDK
|
||||
- VAAPI support via Mesa `radeonsi` driver
|
||||
- VCN 4.0+ competitive with NVENC in quality
|
||||
|
||||
### Key Design Insights for WayRay
|
||||
|
||||
- **Start with H.264** for maximum compatibility, add H.265/AV1 as options
|
||||
- Use **VAAPI** as the primary encoding API (works across Intel/AMD, open-source)
|
||||
- Add NVENC support via FFmpeg/GStreamer for NVIDIA GPUs
|
||||
- **CBR + no B-frames + gradual intra refresh** for lowest latency
|
||||
- AV1's screen content coding mode is a significant advantage for desktop content
|
||||
- The **DMA-BUF -> VAAPI encode** path is zero-copy and should be the primary pipeline
|
||||
|
||||
### Sources
|
||||
- [NVIDIA Video Codec SDK](https://developer.nvidia.com/video-codec-sdk)
|
||||
- [NVENC Application Note](https://docs.nvidia.com/video-technologies/video-codec-sdk/13.0/nvenc-application-note/index.html)
|
||||
- [NVIDIA AV1 Blog Post](https://developer.nvidia.com/blog/improving-video-quality-and-performance-with-av1-and-nvidia-ada-lovelace-architecture/)
|
||||
- [GPU Video Encoder Evaluation](https://arxiv.org/html/2511.18688v2)
|
||||
- [VA-API Intel Documentation](https://intel.github.io/libva/)
|
||||
- [Hardware Video Acceleration ArchWiki](https://wiki.archlinux.org/title/Hardware_video_acceleration)
|
||||
- [Chrome AV1 Improvements](https://developer.chrome.com/blog/av1)
|
||||
- [CBR vs VBR for Game Streaming](https://pulsegeek.com/articles/cbr-vs-vbr-for-low-latency-game-streaming/)
|
||||
- [AV1 SVC in WebRTC](https://w3c.github.io/webrtc-svc/)
|
||||
|
||||
---
|
||||
|
||||
## 7. Network Protocols
|
||||
|
||||
### TCP vs. UDP vs. QUIC for Remote Display
|
||||
|
||||
| Property | TCP | UDP | QUIC |
|
||||
|---|---|---|---|
|
||||
| **Reliability** | Full (retransmit) | None | Selectable per-stream |
|
||||
| **Head-of-line blocking** | Yes (single stream) | No | No (multiplexed streams) |
|
||||
| **Connection setup** | 1-3 RTT (TCP + TLS) | 0 RTT | 0-1 RTT |
|
||||
| **Congestion control** | Kernel-space, slow to update | Application-managed | User-space, pluggable |
|
||||
| **NAT/firewall traversal** | Good | Moderate | Moderate (UDP-based) |
|
||||
| **Encryption** | Optional (TLS) | Optional (DTLS) | Mandatory (TLS 1.3) |
|
||||
|
||||
### QUIC Advantages for Remote Display
|
||||
|
||||
QUIC is increasingly compelling for remote display:
|
||||
|
||||
1. **Stream multiplexing without HOL blocking**: Display, input, audio can be separate QUIC streams. A lost display packet doesn't stall input delivery.
|
||||
2. **0-RTT connection setup**: Critical for session resumption / hot-desking scenarios
|
||||
3. **Pluggable congestion control**: Can use algorithms optimized for low-latency interactive traffic (e.g., BBR, COPA)
|
||||
4. **Connection migration**: Session survives network changes (WiFi -> Ethernet)
|
||||
|
||||
### QUIC Challenges
|
||||
|
||||
- **Firewall blocking**: Some corporate networks block UDP, forcing TCP fallback. The fallback penalty is severe (full session teardown + TCP reconnect).
|
||||
- **Library maturity**: QUIC implementations are still maturing. Key libraries:
|
||||
- **quinn** (Rust): Well-maintained, async, good for our use case
|
||||
- **quiche** (Cloudflare, Rust/C): Production-tested
|
||||
- **s2n-quic** (AWS, Rust): High performance
|
||||
- **CPU overhead**: QUIC's encryption and user-space processing can be higher than kernel TCP
|
||||
|
||||
### Media over QUIC (MoQ)
|
||||
|
||||
MoQ is an emerging IETF standard (RFC expected 2026) that combines:
|
||||
- Low-latency interactivity of WebRTC
|
||||
- Scalability of HLS/DASH
|
||||
- Built on QUIC/WebTransport
|
||||
|
||||
**Architecture**: Publish-subscribe model with tracks, groups, and objects. Sub-250ms latency target.
|
||||
|
||||
**Relevance**: MoQ's concepts (prioritized streams, partial reliability, adaptive quality) are directly applicable to remote display, though the protocol itself is focused on media distribution rather than interactive desktop.
|
||||
|
||||
**Implementations**: Cloudflare has deployed MoQ relays on their global network. OpenMOQ consortium (Akamai, Cisco, YouTube, etc.) developing open source implementations.
|
||||
|
||||
### Adaptive Bitrate for Remote Display
|
||||
|
||||
Key strategies:
|
||||
- **Bandwidth estimation**: Measure RTT and throughput continuously
|
||||
- **Quality adjustment**: Change encoder bitrate, resolution, or frame rate
|
||||
- **Frame dropping**: Under extreme congestion, drop non-reference frames
|
||||
- **Temporal scalability (SVC)**: Encode with multiple temporal layers, drop higher layers under congestion
|
||||
- **Resolution scaling**: Encode at lower resolution and upscale on client (works well with modern upscaling algorithms)
|
||||
|
||||
### Latency Budget
|
||||
|
||||
For interactive remote desktop, the target end-to-end latency budget:
|
||||
|
||||
| Stage | Target |
|
||||
|---|---|
|
||||
| Capture | <1ms (DMA-BUF) |
|
||||
| Encode | 1-5ms (hardware) |
|
||||
| Network (LAN) | <1ms |
|
||||
| Network (WAN) | 10-100ms |
|
||||
| Decode | 1-3ms (hardware) |
|
||||
| Render | <1ms |
|
||||
| **Total (LAN)** | **<10ms** |
|
||||
| **Total (WAN)** | **15-110ms** |
|
||||
|
||||
### Key Design Insights for WayRay
|
||||
|
||||
- **Use QUIC as primary transport** with TCP fallback
|
||||
- Rust has excellent QUIC libraries (quinn)
|
||||
- Separate QUIC streams for display, input, audio, USB
|
||||
- Input should be highest priority (lowest latency)
|
||||
- Implement adaptive bitrate from the start
|
||||
- Consider SVC temporal layers in the encoder for graceful degradation
|
||||
|
||||
### Sources
|
||||
- [Media Over QUIC IETF Working Group](https://datatracker.ietf.org/group/moq/about/)
|
||||
- [Cloudflare MoQ Blog](https://blog.cloudflare.com/moq/)
|
||||
- [Streaming Remote Rendering: QUIC vs WebRTC](https://arxiv.org/html/2505.22132v1)
|
||||
- [MOQ Protocol Explained - WebRTC.ventures](https://webrtc.ventures/2025/10/moq-protocol-explained-unifying-real-time-and-scalable-streaming/)
|
||||
- [MoQ - nanocosmos](https://www.nanocosmos.net/blog/media-over-quic-moq/)
|
||||
- [QUIC Fix for Video Streaming](https://arxiv.org/pdf/1809.10270)
|
||||
|
||||
---
|
||||
|
||||
## 8. Framebuffer Capture Techniques
|
||||
|
||||
### DMA-BUF Export (Zero-Copy)
|
||||
|
||||
**DMA-BUF** is the Linux kernel subsystem for sharing buffers between devices (GPU, display, video encoder).
|
||||
|
||||
**How it works**:
|
||||
1. GPU renders frame into a DMA-BUF object (fd-backed GPU memory)
|
||||
2. The fd is passed to the consumer (encoder, another GPU, etc.)
|
||||
3. No CPU copy occurs; the buffer stays in GPU memory
|
||||
|
||||
**For a Wayland compositor acting as a thin client server**:
|
||||
```
|
||||
[Wayland clients] --> [Compositor renders to GPU buffer]
|
||||
|
|
||||
[DMA-BUF export (fd)]
|
||||
|
|
||||
[VAAPI encoder imports fd]
|
||||
|
|
||||
[Encoded bitstream -> network]
|
||||
```
|
||||
|
||||
**Key protocols**:
|
||||
- `linux-dmabuf-v1`: Clients use this to submit GPU-rendered buffers to the compositor
|
||||
- `ext-image-copy-capture-v1`: Captures compositor output as DMA-BUF
|
||||
- DMA-BUF feedback (v4): Tells clients which GPU/format the compositor prefers
|
||||
|
||||
### GPU Readback (Fallback)
|
||||
|
||||
When DMA-BUF export is not possible:
|
||||
1. Compositor renders to GPU texture
|
||||
2. `glReadPixels()` or equivalent copies pixels to CPU memory
|
||||
3. CPU memory is then compressed/encoded
|
||||
|
||||
This is **significantly slower** due to the GPU -> CPU copy and pipeline stall, but universally supported.
|
||||
|
||||
### Damage Tracking
|
||||
|
||||
**Damage tracking** identifies which regions of the screen changed between frames, avoiding retransmission of unchanged areas.
|
||||
|
||||
**Wayland's built-in damage tracking**:
|
||||
- Each `wl_surface.commit()` includes damage rectangles via `wl_surface.damage()` or `wl_surface.damage_buffer()`
|
||||
- The compositor knows exactly which surface regions changed
|
||||
|
||||
**Compositor-level damage**:
|
||||
- The compositor tracks which regions of the output changed (due to surface damage, window moves, overlapping windows, etc.)
|
||||
- `ext-image-copy-capture-v1` supports damage reporting: the compositor tells the capturer which regions changed since the last frame
|
||||
|
||||
**For encoding efficiency**:
|
||||
- With H.264/H.265/AV1: damage regions inform the encoder which macroblocks to mark as changed
|
||||
- With lossless compression: only changed regions need to be compressed and sent
|
||||
- With hybrid approach: unchanged regions get zero bits, changed regions get full encoding
|
||||
|
||||
### wl-screenrec: Reference Implementation
|
||||
|
||||
[wl-screenrec](https://github.com/russelltg/wl-screenrec) is a Rust project demonstrating high-performance Wayland screen recording:
|
||||
- Uses wlr-screencopy with DMA-BUF
|
||||
- Hardware encoding via VAAPI
|
||||
- Zero-copy pipeline (DMA-BUF -> VAAPI -> file)
|
||||
- Written in Rust, good reference for our implementation
|
||||
|
||||
### Key Design Insights for WayRay
|
||||
|
||||
- **Own the compositor**: By building/extending a Wayland compositor, we have direct access to all rendering state, damage information, and DMA-BUF handles
|
||||
- **DMA-BUF -> VAAPI is the critical path**: This zero-copy pipeline should be the primary encoding path
|
||||
- **Damage tracking reduces encoding work**: Use Wayland's built-in damage tracking to minimize what gets encoded
|
||||
- **Fallback to GPU readback** for unsupported hardware
|
||||
- **wl-screenrec** is a good Rust reference for the capture -> encode pipeline
|
||||
|
||||
### Sources
|
||||
- [Linux DMA-BUF Kernel Documentation](https://dri.freedesktop.org/docs/drm/driver-api/dma-buf.html)
|
||||
- [Linux DMA-BUF Wayland Protocol](https://wayland-book.com/surfaces/dmabuf.html)
|
||||
- [ext-image-copy-capture-v1](https://wayland.app/protocols/ext-image-copy-capture-v1)
|
||||
- [wlr-export-dmabuf-unstable-v1](https://wayland.app/protocols/wlr-export-dmabuf-unstable-v1)
|
||||
- [wl-screenrec GitHub](https://github.com/russelltg/wl-screenrec)
|
||||
- [OBS Zero-Copy Capture](https://obsproject.com/forum/threads/experimental-zero-copy-screen-capture-on-linux.101262/)
|
||||
- [GStreamer DMA-BUF Design](https://gstreamer.freedesktop.org/documentation/additional/design/dmabuf.html)
|
||||
|
||||
---
|
||||
|
||||
## 9. Audio Forwarding
|
||||
|
||||
### PipeWire Network Audio
|
||||
|
||||
PipeWire provides several mechanisms for network audio:
|
||||
|
||||
#### RTP Modules (Recommended)
|
||||
|
||||
**`module-rtp-sink`**: Creates a PipeWire sink that sends audio as RTP packets
|
||||
- Supports raw PCM, Opus encoding
|
||||
- Configurable latency via `sess.latency.msec` (default: 100ms for network)
|
||||
- Uses SAP/mDNS for discovery
|
||||
|
||||
**`module-rtp-source`**: Creates a PipeWire source that receives RTP packets
|
||||
- DLL-based clock recovery to handle network jitter
|
||||
- Configurable ring buffer fill level
|
||||
|
||||
**`module-rtp-session`**: Combined send/receive with automatic discovery
|
||||
- Uses Apple MIDI protocol for low-latency bidirectional MIDI
|
||||
- Announced via Avahi/mDNS/Bonjour
|
||||
|
||||
#### Pulse Tunnel Module
|
||||
|
||||
**`module-pulse-tunnel`**: Tunnels audio to/from a remote PulseAudio/PipeWire-Pulse server
|
||||
- Simpler setup, works over TCP
|
||||
- Higher latency than RTP approach
|
||||
- Good for compatibility with existing PulseAudio setups
|
||||
|
||||
### Low-Latency Audio Considerations
|
||||
|
||||
For remote desktop audio, the targets are:
|
||||
|
||||
| Parameter | Target |
|
||||
|---|---|
|
||||
| **Codec** | Opus (designed for low latency) |
|
||||
| **Frame size** | 2.5ms - 10ms (Opus supports down to 2.5ms) |
|
||||
| **Buffer/Quantum** | As low as 128 samples @ 48kHz (~2.67ms) |
|
||||
| **Network jitter buffer** | 10-30ms |
|
||||
| **Total one-way latency** | 15-50ms |
|
||||
|
||||
**Opus codec advantages**:
|
||||
- Designed for both speech and music
|
||||
- 2.5ms to 60ms frame sizes
|
||||
- 6 kbps to 510 kbps bitrate range
|
||||
- Built-in forward error correction (FEC)
|
||||
- Packet loss concealment (PLC)
|
||||
|
||||
### Custom Audio Pipeline for Thin Client
|
||||
|
||||
For a purpose-built thin client, the audio pipeline should be:
|
||||
|
||||
```
|
||||
[Server PipeWire] -> [Opus encode] -> [RTP/QUIC] -> [Opus decode] -> [Client audio output]
|
||||
[Client microphone] -> [Opus encode] -> [RTP/QUIC] -> [Opus decode] -> [Server PipeWire]
|
||||
```
|
||||
|
||||
Key considerations:
|
||||
- **Clock synchronization**: Client and server audio clocks will drift. Need adaptive resampling or buffer management.
|
||||
- **Jitter compensation**: Network jitter requires a playout buffer. Adaptive jitter buffer adjusts to network conditions.
|
||||
- **Echo cancellation**: If microphone and speakers are on the same client device, need AEC.
|
||||
|
||||
### Key Design Insights for WayRay
|
||||
|
||||
- **Opus over QUIC** is the right approach for a custom thin client
|
||||
- PipeWire's RTP module is a good starting point but we may want tighter integration
|
||||
- Clock drift compensation is critical for long-running sessions
|
||||
- Audio and video synchronization (lip sync) must be maintained
|
||||
- Forward error correction helps with packet loss without retransmission latency
|
||||
|
||||
### Sources
|
||||
- [PipeWire RTP Session Module](https://docs.pipewire.org/page_module_rtp_session.html)
|
||||
- [PipeWire RTP Sink](https://docs.pipewire.org/page_module_rtp_sink.html)
|
||||
- [PipeWire RTP Source](https://docs.pipewire.org/page_module_rtp_source.html)
|
||||
- [PipeWire Pulse Tunnel](https://docs.pipewire.org/page_module_pulse_tunnel.html)
|
||||
- [PipeWire/PulseAudio RTP Network Audio Guide (Oct 2025)](https://liotier.medium.com/pipewire-pulseaudio-rtp-network-audio-in-october-2025-a-configuration-guide-to-the-remote-time-e8dc0e20e3b0)
|
||||
- [PipeWire ArchWiki](https://wiki.archlinux.org/title/PipeWire)
|
||||
- [PulseAudio Network Setup](https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/Network/)
|
||||
|
||||
---
|
||||
|
||||
## 10. USB/IP
|
||||
|
||||
### Architecture
|
||||
|
||||
USB/IP is a Linux kernel subsystem that shares USB devices over TCP/IP networks.
|
||||
|
||||
**Components**:
|
||||
|
||||
| Component | Side | Role |
|
||||
|---|---|---|
|
||||
| **usbip-core** | Both | Shared protocol and utility code |
|
||||
| **vhci-hcd** | Client | Virtual Host Controller Interface - presents virtual USB ports to the local USB stack |
|
||||
| **usbip-host** (stub) | Server | Binds to physical USB devices, encapsulates URBs for network transmission |
|
||||
| **usbip-vudc** | Server | Virtual USB Device Controller, for USB Gadget-based virtual devices |
|
||||
|
||||
### Protocol
|
||||
|
||||
**Discovery**: Client sends `OP_REQ_DEVLIST` over TCP, server responds with `OP_REP_DEVLIST` listing exportable devices.
|
||||
|
||||
**Attachment**: Client sends `OP_REQ_IMPORT`, server responds with `OP_REP_IMPORT` and begins forwarding URBs.
|
||||
|
||||
**Data transfer**: USB Request Blocks (URBs) are encapsulated in TCP packets and forwarded between stub driver and VHCI. The device driver runs entirely on the **client** side.
|
||||
|
||||
**Port**: TCP 3240 (default)
|
||||
|
||||
### Protocol Flow
|
||||
|
||||
```
|
||||
[USB Device] <-> [Stub Driver (server kernel)]
|
||||
|
|
||||
[TCP/IP Network]
|
||||
|
|
||||
[VHCI Driver (client kernel)]
|
||||
|
|
||||
[USB Device Driver (client)]
|
||||
|
|
||||
[Application (client)]
|
||||
```
|
||||
|
||||
### Kernel Integration
|
||||
|
||||
- Merged into mainline Linux since **kernel 3.17**
|
||||
- Source: `drivers/usb/usbip/` and `tools/usb/usbip/`
|
||||
- Supports USB 2.0 and USB 3.0 devices
|
||||
- Windows support via [usbip-win](https://github.com/cezanne/usbip-win)
|
||||
|
||||
### Limitations
|
||||
|
||||
- **Latency**: TCP round-trip for every URB can add significant latency for isochronous devices (audio, video)
|
||||
- **Bandwidth**: USB 3.0 bulk transfers work well, but sustained high-bandwidth is limited by network
|
||||
- **Isochronous transfers**: Not well supported (real-time USB audio/video devices may not work)
|
||||
- **Security**: No built-in encryption (must tunnel through SSH/VPN)
|
||||
|
||||
### Alternatives: SPICE usbredir
|
||||
|
||||
SPICE's USB redirection (`usbredir`) is an alternative approach:
|
||||
- Library: `libusbredir`
|
||||
- Works at the USB protocol level (like USB/IP)
|
||||
- Better integration with SPICE's authentication/encryption
|
||||
- Can be used independently of SPICE
|
||||
|
||||
### Key Design Insights for WayRay
|
||||
|
||||
- **USB/IP is mature and kernel-integrated** - good baseline
|
||||
- For a thin client, wrapping USB/IP over QUIC (instead of raw TCP) would add encryption and better congestion handling
|
||||
- **usbredir** is worth considering as it's designed for remote desktop use cases
|
||||
- Isochronous USB devices (webcams, audio interfaces) are challenging over network and may need special handling
|
||||
- Consider selective USB forwarding - only forward devices the user explicitly shares
|
||||
|
||||
### Sources
|
||||
- [USB/IP Kernel Documentation](https://docs.kernel.org/usb/usbip_protocol.html)
|
||||
- [USB/IP ArchWiki](https://wiki.archlinux.org/title/USB/IP)
|
||||
- [USB/IP Project](https://usbip.sourceforge.net/)
|
||||
- [Linux Kernel USB/IP Source](https://github.com/torvalds/linux/tree/master/tools/usb/usbip)
|
||||
- [USB/IP Tutorial - Linux Magazine](https://www.linux-magazine.com/Issues/2018/208/Tutorial-USB-IP)
|
||||
- [usbip-win (Windows Support)](https://github.com/cezanne/usbip-win)
|
||||
- [VirtualHere (Commercial Alternative)](https://www.virtualhere.com/)
|
||||
|
||||
---
|
||||
|
||||
## 11. Modern Thin Client Projects
|
||||
|
||||
### Sun Ray (Historical Reference)
|
||||
|
||||
The original Sun Ray (1999-2014) is the gold standard for thin client architecture:
|
||||
|
||||
- **Protocol**: Appliance Link Protocol (ALP) over UDP/IP
|
||||
- **Architecture**: Completely stateless DTUs (Desktop Terminal Units) with zero local storage/OS
|
||||
- **Session model**: Sessions are independent of physical hardware. Pull your smartcard, insert at another Sun Ray, session follows instantly ("hot desking")
|
||||
- **Server**: Sun Ray Server Software (SRSS) managed sessions, ran on Solaris/Linux
|
||||
- **Network**: Standard switched Ethernet, DHCP-based configuration
|
||||
- **Security**: SSL/TLS encryption with 128-bit ARCFOUR
|
||||
- **Display**: Rendered entirely on server, compressed framebuffer sent to DTU
|
||||
|
||||
**Key Sun Ray concepts to replicate**:
|
||||
- Instant session mobility (smartcard/badge driven)
|
||||
- Zero client-side state
|
||||
- Centralized session management
|
||||
- Simple, robust network boot
|
||||
|
||||
### Wafer (Wayland-Based Thin Client)
|
||||
|
||||
[Wafer](https://github.com/lp-programming/Wafer) is the most directly comparable modern project:
|
||||
|
||||
- **Goal**: Thin client for Linux server + Linux clients over high-speed LAN
|
||||
- **Protocol**: Wayland protocol over network
|
||||
- **Server** ("Mainframe"): Multi-core machine with GBM-capable GPU
|
||||
- **Design**: Full 3D acceleration on server, minimal CPU on client (Raspberry Pi target)
|
||||
- **Status**: Proof of concept / early development
|
||||
|
||||
### Sunshine + Moonlight
|
||||
|
||||
[Sunshine](https://github.com/LizardByte/Sunshine) (server) + [Moonlight](https://moonlight-stream.org/) (client) is the most mature open-source streaming solution:
|
||||
|
||||
- **Protocol**: Based on NVIDIA GameStream protocol
|
||||
- **Encoding**: H.264, H.265, AV1 with NVENC, VAAPI, AMF hardware encoding
|
||||
- **Performance**: Sub-10ms latency on LAN, up to 120 FPS
|
||||
- **Clients**: Android, iOS, PC, Mac, Raspberry Pi, Steam Deck, Nintendo Switch, LG webOS
|
||||
- **Audio**: Full audio streaming with multi-channel support
|
||||
- **Input**: Mouse, keyboard, gamepad, touchscreen
|
||||
- **Limitations**: Designed for single-user gaming, not multi-user thin client
|
||||
|
||||
### WayVNC
|
||||
|
||||
[WayVNC](https://github.com/any1/wayvnc) is a VNC server for wlroots-based Wayland compositors:
|
||||
|
||||
- Implements RFB protocol over wlr-screencopy / ext-image-copy-capture
|
||||
- Supports headless mode (no physical display)
|
||||
- Authentication: PAM, TLS (VeNCrypt), RSA-AES
|
||||
- Input: Virtual pointer and keyboard via Wayland protocols
|
||||
- JSON-IPC for runtime control
|
||||
- Good reference for Wayland compositor integration
|
||||
|
||||
### GNOME Remote Desktop
|
||||
|
||||
GNOME's built-in remote desktop solution:
|
||||
|
||||
- Speaks **RDP** (primary) and VNC
|
||||
- Uses PipeWire for screen capture via Mutter's ScreenCast D-Bus API
|
||||
- Supports headless multi-user sessions (GNOME 46+)
|
||||
- Input forwarding via Mutter's RemoteDesktop D-Bus API
|
||||
- Integrated with GDM for remote login
|
||||
- Active development, improving rapidly
|
||||
|
||||
### ThinStation
|
||||
|
||||
[ThinStation](https://thinstation.github.io/thinstation/) is a framework for building thin client Linux images:
|
||||
|
||||
- Supports Citrix ICA, SPICE, NX, RDP, VMware Horizon
|
||||
- Boots from network (PXE), USB, or compact flash
|
||||
- Not a protocol itself, but a client OS/distribution
|
||||
|
||||
### openthinclient
|
||||
|
||||
[openthinclient](https://openthinclient.com/) is a commercial open-source thin client management platform:
|
||||
|
||||
- Based on Debian (latest: Debian 13 "Trixie")
|
||||
- Manages thin client fleet, user sessions, applications
|
||||
- Supports multiple VDI protocols
|
||||
- Version 2603 (2025) includes updated VDI components
|
||||
|
||||
### Key Design Insights for WayRay
|
||||
|
||||
- **Sunshine/Moonlight** proves that low-latency game streaming is solved; adapt for desktop
|
||||
- **WayVNC** shows how to integrate with wlroots compositors
|
||||
- **GNOME Remote Desktop** shows the PipeWire + portal approach
|
||||
- **Wafer** validates the concept but is early-stage
|
||||
- **Sun Ray's session mobility** is the killer feature to replicate
|
||||
- No existing project combines: Wayland-native + multi-user + session mobility + hardware encoding + QUIC transport
|
||||
|
||||
### Sources
|
||||
- [Sun Ray Wikipedia](https://en.wikipedia.org/wiki/Sun_Ray)
|
||||
- [Sun Ray System Overview - Oracle](https://docs.oracle.com/cd/E19634-01/820-0411/overview.html)
|
||||
- [Using Sun Ray Thin Clients in 2025](https://catstret.ch/202506/sun-ray-shenanigans/)
|
||||
- [Wafer GitHub](https://github.com/lp-programming/Wafer)
|
||||
- [Sunshine GitHub](https://github.com/LizardByte/Sunshine)
|
||||
- [Moonlight](https://moonlight-stream.org/)
|
||||
- [WayVNC GitHub](https://github.com/any1/wayvnc)
|
||||
- [GNOME Remote Desktop Wiki](https://wiki.gnome.org/Projects/Mutter/RemoteDesktop)
|
||||
- [ThinStation](https://thinstation.github.io/thinstation/)
|
||||
- [openthinclient](https://openthinclient.com/)
|
||||
|
||||
---
|
||||
|
||||
## 12. Architecture Recommendations for a SunRay-Like System
|
||||
|
||||
Based on all the research above, here is a synthesized architectural recommendation:
|
||||
|
||||
### Core Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ WayRay Server │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────┐ │
|
||||
│ │ Wayland Compositor (wlroots) │ │
|
||||
│ │ - Per-user session │ │
|
||||
│ │ - DMA-BUF output │ │
|
||||
│ │ - Damage tracking │ │
|
||||
│ └──────────┬──────────────────────┘ │
|
||||
│ │ DMA-BUF (zero-copy) │
|
||||
│ ┌──────────▼──────────────────────┐ │
|
||||
│ │ Encoder Pipeline │ │
|
||||
│ │ - VAAPI H.264/H.265/AV1 │ │
|
||||
│ │ - Damage-aware encoding │ │
|
||||
│ │ - Adaptive bitrate │ │
|
||||
│ └──────────┬──────────────────────┘ │
|
||||
│ │ Encoded frames │
|
||||
│ ┌──────────▼──────────────────────┐ │
|
||||
│ │ Session Manager │ │
|
||||
│ │ - Multi-user sessions │ │
|
||||
│ │ - Session migration │ │
|
||||
│ │ - Authentication │ │
|
||||
│ └──────────┬──────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌──────────▼──────────────────────┐ │
|
||||
│ │ QUIC Transport │ │
|
||||
│ │ - Display stream (video) │ │
|
||||
│ │ - Input stream (low-latency) │ │
|
||||
│ │ - Audio stream (Opus/RTP) │ │
|
||||
│ │ - USB stream (usbredir) │ │
|
||||
│ │ - Control stream │ │
|
||||
│ └──────────┬──────────────────────┘ │
|
||||
└─────────────┼───────────────────────────────┘
|
||||
│ QUIC / Network
|
||||
┌─────────────┼───────────────────────────────┐
|
||||
│ │ │
|
||||
│ ┌──────────▼──────────────────────┐ │
|
||||
│ │ QUIC Transport │ │
|
||||
│ └──────────┬──────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌──────────▼──────────────────────┐ │
|
||||
│ │ Decoder (VAAPI/SW) │ │
|
||||
│ │ + Audio (Opus decode) │ │
|
||||
│ │ + Input capture │ │
|
||||
│ │ + USB forwarding │ │
|
||||
│ └──────────┬──────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌──────────▼──────────────────────┐ │
|
||||
│ │ Minimal Wayland Compositor │ │
|
||||
│ │ (or direct DRM/KMS output) │ │
|
||||
│ └─────────────────────────────────┘ │
|
||||
│ WayRay Client │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Technology Stack Recommendations
|
||||
|
||||
| Component | Recommended Technology | Rationale |
|
||||
|---|---|---|
|
||||
| **Server compositor** | wlroots-based custom compositor | Direct access to DMA-BUF, damage tracking, input injection |
|
||||
| **Capture** | Direct compositor integration (no protocol needed) | Lowest latency, full damage info |
|
||||
| **Encoding** | VAAPI (primary), NVENC (optional) via FFmpeg/GStreamer | Cross-vendor, zero-copy from DMA-BUF |
|
||||
| **Video codec** | H.264 (default), AV1 (preferred when supported) | H.264 for compatibility, AV1 for quality/bandwidth |
|
||||
| **Transport** | QUIC (quinn crate) with TCP fallback | Low latency, multiplexing, 0-RTT |
|
||||
| **Audio** | Opus over QUIC stream | Low latency, built-in FEC |
|
||||
| **USB** | usbredir over QUIC stream | Designed for remote desktop |
|
||||
| **Session management** | Custom (inspired by Sun Ray SRSS) | Session mobility, multi-user |
|
||||
| **Client display** | DRM/KMS direct or minimal Wayland compositor | Minimal overhead |
|
||||
| **Language** | Rust | Safety, performance, excellent ecosystem (smithay, quinn, etc.) |
|
||||
|
||||
### QUIC Stream Layout
|
||||
|
||||
| Stream ID | Type | Priority | Reliability | Content |
|
||||
|---|---|---|---|---|
|
||||
| 0 | Bidirectional | Highest | Reliable | Control/session management |
|
||||
| 1 | Server -> Client | High | Unreliable | Video frames |
|
||||
| 2 | Client -> Server | Highest | Reliable | Input events |
|
||||
| 3 | Server -> Client | Medium | Reliable | Audio playback |
|
||||
| 4 | Client -> Server | Medium | Reliable | Audio capture |
|
||||
| 5 | Bidirectional | Low | Reliable | USB/IP data |
|
||||
| 6 | Bidirectional | Medium | Reliable | Clipboard |
|
||||
|
||||
### Encoding Strategy
|
||||
|
||||
1. **Damage detection**: Compositor reports damaged regions per frame
|
||||
2. **Content classification**: Heuristically detect video regions vs. desktop content (like SPICE does)
|
||||
3. **Encoding decision**:
|
||||
- Small damage, text/UI: Lossless (zstd-compressed) tile updates
|
||||
- Large damage, desktop: H.264/AV1 with high quality, low bitrate
|
||||
- Video regions: H.264/AV1 with lower quality, higher frame rate
|
||||
- Full screen video: Full-frame H.264/AV1 encoding
|
||||
4. **Adaptive quality**: Adjust based on measured bandwidth and latency
|
||||
|
||||
### Sun Ray Features to Implement
|
||||
|
||||
1. **Session mobility**: Associate sessions with authentication tokens, not hardware. Insert token at any client -> session follows.
|
||||
2. **Stateless clients**: Client boots from network, has no persistent state.
|
||||
3. **Centralized management**: Server manages all sessions, client configurations, authentication.
|
||||
4. **Hot desking**: Disconnect from one client, connect at another, session is exactly where you left it.
|
||||
5. **Multi-monitor**: Support multiple displays per session.
|
||||
15
book/book.toml
Normal file
15
book/book.toml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
[book]
|
||||
authors = ["WayRay Contributors"]
|
||||
language = "en"
|
||||
multilingual = false
|
||||
src = "src"
|
||||
title = "WayRay - Thin Client Wayland Compositor"
|
||||
description = "A modern SunRay-like thin client system built on Wayland and Rust"
|
||||
|
||||
[build]
|
||||
build-dir = "build"
|
||||
|
||||
[output.html]
|
||||
default-theme = "navy"
|
||||
preferred-dark-theme = "navy"
|
||||
git-repository-url = "https://github.com/wayray/wayray"
|
||||
44
book/src/SUMMARY.md
Normal file
44
book/src/SUMMARY.md
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# Summary
|
||||
|
||||
[Introduction](./introduction.md)
|
||||
|
||||
# Getting Started
|
||||
|
||||
- [Overview](./overview.md)
|
||||
- [Installation](./installation.md)
|
||||
- [Quick Start](./quickstart.md)
|
||||
|
||||
# Concepts
|
||||
|
||||
- [Architecture](./concepts/architecture.md)
|
||||
- [Session Mobility](./concepts/session-mobility.md)
|
||||
- [Display Encoding](./concepts/display-encoding.md)
|
||||
- [Network Transport](./concepts/network-transport.md)
|
||||
- [Pluggable Window Management](./concepts/pluggable-wm.md)
|
||||
|
||||
# Server Guide
|
||||
|
||||
- [Server Configuration](./server/configuration.md)
|
||||
- [Running the Server](./server/running.md)
|
||||
- [Multi-Server Setup](./server/multi-server.md)
|
||||
- [Session Management](./server/sessions.md)
|
||||
|
||||
# Client Guide
|
||||
|
||||
- [Client Configuration](./client/configuration.md)
|
||||
- [Connecting to a Server](./client/connecting.md)
|
||||
- [Token Setup](./client/tokens.md)
|
||||
- [Audio & USB](./client/peripherals.md)
|
||||
|
||||
# Administration
|
||||
|
||||
- [wayray-ctl Reference](./admin/wayray-ctl.md)
|
||||
- [Security](./admin/security.md)
|
||||
- [Monitoring](./admin/monitoring.md)
|
||||
- [Troubleshooting](./admin/troubleshooting.md)
|
||||
|
||||
# Development
|
||||
|
||||
- [Building from Source](./dev/building.md)
|
||||
- [Protocol Reference](./dev/protocol.md)
|
||||
- [Contributing](./dev/contributing.md)
|
||||
23
book/src/admin/monitoring.md
Normal file
23
book/src/admin/monitoring.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Monitoring
|
||||
|
||||
> This page will be expanded as monitoring features are implemented.
|
||||
|
||||
## Metrics
|
||||
|
||||
WayRay exposes metrics for monitoring:
|
||||
|
||||
- Active sessions count
|
||||
- Frame encoding latency (p50, p95, p99)
|
||||
- Network bandwidth per session
|
||||
- Audio latency
|
||||
- CPU and memory usage per session
|
||||
|
||||
## Logging
|
||||
|
||||
WayRay uses structured logging via the `tracing` crate. Configure log levels via `RUST_LOG`:
|
||||
|
||||
```bash
|
||||
RUST_LOG=wayray=info wayray-server # Standard
|
||||
RUST_LOG=wayray=debug wayray-server # Verbose
|
||||
RUST_LOG=wayray=trace wayray-server # Everything
|
||||
```
|
||||
25
book/src/admin/security.md
Normal file
25
book/src/admin/security.md
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# Security
|
||||
|
||||
> This page will be expanded as security features are implemented.
|
||||
|
||||
## Encryption
|
||||
|
||||
All WayRay traffic is encrypted via TLS 1.3 (mandatory in QUIC). There is no unencrypted mode.
|
||||
|
||||
## Authentication
|
||||
|
||||
- User authentication via PAM (supports LDAP, Kerberos, local accounts)
|
||||
- Session binding via tokens
|
||||
- Optional multi-factor authentication
|
||||
|
||||
## Client Security
|
||||
|
||||
- No data at rest on client devices
|
||||
- No persistent storage of credentials
|
||||
- Optional: lock session on token removal
|
||||
|
||||
## Network Security
|
||||
|
||||
- TLS 1.3 with strong cipher suites
|
||||
- Certificate pinning supported
|
||||
- Optional mutual TLS (client certificates)
|
||||
25
book/src/admin/troubleshooting.md
Normal file
25
book/src/admin/troubleshooting.md
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# Troubleshooting
|
||||
|
||||
> This page will be expanded as common issues are identified.
|
||||
|
||||
## Common Issues
|
||||
|
||||
### Client can't connect to server
|
||||
- Check that UDP port 4433 is open
|
||||
- Verify TLS certificates are correct
|
||||
- Check server logs: `RUST_LOG=wayray=debug wayray-server`
|
||||
|
||||
### High latency / poor responsiveness
|
||||
- Check network latency: `ping <server>`
|
||||
- Verify server encoding settings (try `strategy = "lossless"` for LAN)
|
||||
- Monitor server CPU usage -- encoding may be bottlenecked
|
||||
|
||||
### Audio stuttering
|
||||
- Check network jitter
|
||||
- Increase audio buffer size in client config
|
||||
- Verify PipeWire is running on the server
|
||||
|
||||
### Session doesn't resume
|
||||
- Check session timeout hasn't expired
|
||||
- Verify using the same token
|
||||
- Check server logs for session lifecycle events
|
||||
23
book/src/admin/wayray-ctl.md
Normal file
23
book/src/admin/wayray-ctl.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# wayray-ctl Reference
|
||||
|
||||
> This page will be expanded as the management tool is implemented.
|
||||
|
||||
## Commands
|
||||
|
||||
```
|
||||
wayray-ctl
|
||||
session
|
||||
list List all sessions
|
||||
info <id> Show session details
|
||||
kill <id> Terminate a session
|
||||
set-timeout Set session timeout
|
||||
server
|
||||
status Show server status
|
||||
stats Show performance statistics
|
||||
cert
|
||||
generate Generate self-signed TLS certificates
|
||||
group
|
||||
list List servers in the group
|
||||
add <host> Add a server to the group
|
||||
remove <host> Remove a server from the group
|
||||
```
|
||||
30
book/src/client/configuration.md
Normal file
30
book/src/client/configuration.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# Client Configuration
|
||||
|
||||
> This page will be expanded as the client implementation matures.
|
||||
|
||||
## Configuration File
|
||||
|
||||
WayRay client reads configuration from `wayray-client.toml`:
|
||||
|
||||
```toml
|
||||
[connection]
|
||||
server = "192.168.1.100:4433"
|
||||
ca_cert = "/etc/wayray/ca.crt" # Server CA for verification
|
||||
|
||||
[token]
|
||||
type = "software" # "software", "smartcard", or "nfc"
|
||||
value = "my-token-id" # For software tokens
|
||||
|
||||
[display]
|
||||
fullscreen = true
|
||||
scale = 1.0
|
||||
|
||||
[audio]
|
||||
enabled = true
|
||||
input_device = "default"
|
||||
output_device = "default"
|
||||
|
||||
[usb]
|
||||
enabled = true
|
||||
auto_forward = false # Require manual device selection
|
||||
```
|
||||
15
book/src/client/connecting.md
Normal file
15
book/src/client/connecting.md
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# Connecting to a Server
|
||||
|
||||
> This page will be expanded as the client implementation matures.
|
||||
|
||||
## Basic Connection
|
||||
|
||||
```bash
|
||||
wayray-client --server <host>:<port> --token <token>
|
||||
```
|
||||
|
||||
## Connection Troubleshooting
|
||||
|
||||
- Ensure UDP port is open between client and server
|
||||
- Verify TLS certificates are correct
|
||||
- Check server logs for authentication failures
|
||||
26
book/src/client/peripherals.md
Normal file
26
book/src/client/peripherals.md
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# Audio & USB
|
||||
|
||||
> This page will be expanded as peripheral support is implemented.
|
||||
|
||||
## Audio
|
||||
|
||||
Audio is forwarded bidirectionally between server and client using the Opus codec.
|
||||
|
||||
### Output (Server -> Client)
|
||||
Applications on the server produce audio, which is captured, encoded, and played through the client's speakers.
|
||||
|
||||
### Input (Client -> Server)
|
||||
The client's microphone is captured, encoded, and made available as a virtual input device on the server.
|
||||
|
||||
## USB Forwarding
|
||||
|
||||
USB devices connected to the client can be forwarded to the server session.
|
||||
|
||||
### Supported Device Types
|
||||
- Mass storage (flash drives, external drives)
|
||||
- Printers
|
||||
- Scanners
|
||||
- Serial adapters
|
||||
|
||||
### Security
|
||||
USB device forwarding can be restricted by device class at the server level. Administrators can allow or deny specific device types.
|
||||
34
book/src/client/tokens.md
Normal file
34
book/src/client/tokens.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# Token Setup
|
||||
|
||||
> This page will be expanded as token providers are implemented.
|
||||
|
||||
## Software Tokens
|
||||
|
||||
The simplest token type. A UUID is generated and stored on the client.
|
||||
|
||||
```bash
|
||||
# Generate a new software token
|
||||
wayray-client token generate
|
||||
# Output: Token: a1b2c3d4-e5f6-7890-abcd-ef1234567890
|
||||
```
|
||||
|
||||
## Smart Card Tokens
|
||||
|
||||
Requires a PC/SC compatible smart card reader.
|
||||
|
||||
```bash
|
||||
# List available smart card readers
|
||||
wayray-client token list-readers
|
||||
|
||||
# Use smart card for session identity
|
||||
wayray-client --token-type smartcard --server <host>:<port>
|
||||
```
|
||||
|
||||
## NFC Tokens
|
||||
|
||||
Requires an NFC reader.
|
||||
|
||||
```bash
|
||||
# Use NFC for session identity
|
||||
wayray-client --token-type nfc --server <host>:<port>
|
||||
```
|
||||
80
book/src/concepts/architecture.md
Normal file
80
book/src/concepts/architecture.md
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
# Architecture
|
||||
|
||||
WayRay follows a classic thin client architecture with a modern twist: the server is a full Wayland compositor, and the network protocol is QUIC.
|
||||
|
||||
## The Big Picture
|
||||
|
||||
```
|
||||
┌──────────────────────┐ QUIC/TLS 1.3 ┌──────────────────────┐
|
||||
│ WayRay Server │ ◄──────────────────────────► │ WayRay Client │
|
||||
│ │ │ │
|
||||
│ Wayland Compositor │ Display frames ──────────► │ Frame Decoder │
|
||||
│ (Smithay) │ │ + Display (wgpu) │
|
||||
│ │ ◄────────── Input events │ │
|
||||
│ Applications run │ │ Keyboard/Mouse │
|
||||
│ here (foot, firefox │ Audio (Opus) ◄──────────► │ capture │
|
||||
│ vscode, etc.) │ │ │
|
||||
│ │ USB data ◄──────────────► │ USB forwarding │
|
||||
└──────────────────────┘ └──────────────────────┘
|
||||
```
|
||||
|
||||
## Server Architecture
|
||||
|
||||
The server has four major subsystems:
|
||||
|
||||
### 1. Compositor (Smithay)
|
||||
The heart of WayRay. This is a standard Wayland compositor that:
|
||||
- Accepts client connections via the Wayland protocol
|
||||
- Manages window placement, focus, and decoration
|
||||
- Handles input distribution to focused windows
|
||||
- Renders all surfaces into a combined framebuffer
|
||||
|
||||
Unlike a desktop compositor (which renders to a monitor), WayRay's compositor renders to a virtual framebuffer for network transmission.
|
||||
|
||||
### 2. Frame Encoder
|
||||
Takes the rendered framebuffer and produces compressed data for transmission:
|
||||
- **Damage tracking**: Only processes regions that changed since the last frame
|
||||
- **Content classification**: Identifies text, UI, and video regions
|
||||
- **Adaptive encoding**: Lossless for text/UI, lossy for video, based on content and bandwidth
|
||||
- **Tile-based**: Divides the frame into tiles for parallel processing
|
||||
|
||||
### 3. Session Manager
|
||||
Manages the lifecycle of user sessions:
|
||||
- Token-based session identity
|
||||
- Session creation, suspension, and resumption
|
||||
- Multi-server session routing for hot-desking
|
||||
- Authentication via PAM
|
||||
|
||||
### 4. Network Layer (QUIC)
|
||||
Multiplexed transport with independent streams for each data type:
|
||||
- Display, input, audio, and USB on separate streams
|
||||
- Loss on one stream doesn't block others
|
||||
- Built-in encryption (TLS 1.3)
|
||||
- Connection migration and 0-RTT resumption
|
||||
|
||||
## Client Architecture
|
||||
|
||||
The client is intentionally simple -- a "dumb terminal" that:
|
||||
|
||||
1. **Connects** to a server with a session token
|
||||
2. **Receives** encoded frame updates
|
||||
3. **Decodes** and **displays** them via wgpu
|
||||
4. **Captures** input events and sends them to the server
|
||||
5. **Plays** audio received from the server
|
||||
6. **Forwards** USB devices attached to the client
|
||||
|
||||
No application logic runs on the client. If the client crashes or is replaced, nothing is lost.
|
||||
|
||||
## Why This Architecture?
|
||||
|
||||
### Centralized Management
|
||||
All applications, data, and configuration live on servers. IT manages servers, not thousands of desktops.
|
||||
|
||||
### Security
|
||||
Client devices store nothing. A stolen thin client reveals no data. All traffic is encrypted.
|
||||
|
||||
### Session Mobility
|
||||
Sessions are server-side state. Any client can display any session. The token is the key.
|
||||
|
||||
### Resource Efficiency
|
||||
Powerful server hardware is shared among users. Client devices can be minimal -- even a Raspberry Pi.
|
||||
73
book/src/concepts/display-encoding.md
Normal file
73
book/src/concepts/display-encoding.md
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
# Display Encoding
|
||||
|
||||
WayRay uses a content-adaptive encoding strategy to efficiently transmit display updates from server to client.
|
||||
|
||||
## The Challenge
|
||||
|
||||
A 1920x1080 display at 60fps and 32-bit color is approximately 500 MB/s of raw pixel data. Even on a gigabit LAN, this is too much. We need smart compression.
|
||||
|
||||
## Damage Tracking
|
||||
|
||||
The first optimization: don't send what hasn't changed.
|
||||
|
||||
WayRay's Smithay compositor tracks **damage regions** -- the parts of the screen that actually changed between frames. When you type a character in a terminal, only a few pixels change. When a video plays, a rectangular region updates rapidly. When nothing happens, nothing is sent.
|
||||
|
||||
This alone reduces bandwidth by 90%+ for typical desktop use.
|
||||
|
||||
## Tile-Based Encoding
|
||||
|
||||
Damaged regions are divided into 64x64 pixel tiles. Each tile is processed independently, enabling:
|
||||
|
||||
- **Parallel encoding** across CPU cores
|
||||
- **Per-tile codec selection** based on content
|
||||
- **Efficient caching** -- unchanged tiles are never re-encoded
|
||||
|
||||
## Content-Adaptive Compression
|
||||
|
||||
Different content benefits from different encoding:
|
||||
|
||||
### Text and UI (Lossless)
|
||||
Terminal text, code editors, menus, and UI elements must be pixel-perfect. These are encoded using:
|
||||
- XOR diff against the previous frame (most pixels unchanged)
|
||||
- zstd compression of the diff (excellent on sparse data)
|
||||
- Result: perfect quality, very low bandwidth for typical changes
|
||||
|
||||
### Photographic Content (Lossy)
|
||||
Photos, image previews, and complex graphics use:
|
||||
- JPEG or WebP encoding
|
||||
- Quality tuned to available bandwidth
|
||||
- Minor artifacts acceptable; huge bandwidth savings
|
||||
|
||||
### Video Regions (Hardware Encoding)
|
||||
Rapidly changing regions (video playback, animations) use:
|
||||
- H.264 or AV1 video encoding
|
||||
- Hardware acceleration via VAAPI or NVENC when available
|
||||
- Optimized for low latency (no B-frames, constant bitrate)
|
||||
- Detected automatically by tile change frequency
|
||||
|
||||
## Adaptive Bitrate
|
||||
|
||||
WayRay adjusts encoding quality based on network conditions:
|
||||
|
||||
| Condition | Strategy |
|
||||
|-----------|----------|
|
||||
| LAN (< 5ms, > 100 Mbps) | Minimal compression, high framerate |
|
||||
| Good WAN (< 30ms, > 20 Mbps) | Moderate compression, 60fps |
|
||||
| Poor WAN (> 50ms, < 5 Mbps) | Aggressive compression, reduced framerate |
|
||||
| Packet loss detected | Increase keyframe frequency, lower quality |
|
||||
|
||||
The client reports frame decode times and network statistics, allowing the server to adapt in real-time.
|
||||
|
||||
## Frame Pipeline
|
||||
|
||||
```
|
||||
1. Wayland clients commit surface updates
|
||||
2. Compositor renders all surfaces (Pixman or GLES)
|
||||
3. Damage tracker identifies changed tiles
|
||||
4. Each changed tile is classified (text/photo/video)
|
||||
5. Tiles encoded with appropriate codec
|
||||
6. Encoded regions assembled into FrameUpdate message
|
||||
7. Transmitted over QUIC display stream
|
||||
8. Client receives, decodes, and composites onto display
|
||||
9. Client sends FrameAck with decode timing
|
||||
```
|
||||
40
book/src/concepts/network-transport.md
Normal file
40
book/src/concepts/network-transport.md
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# Network Transport
|
||||
|
||||
WayRay uses QUIC as its network transport protocol, providing encrypted, multiplexed, low-latency communication between server and client.
|
||||
|
||||
## Why QUIC?
|
||||
|
||||
### Stream Multiplexing
|
||||
WayRay transmits multiple types of data simultaneously: display frames, input events, audio, and USB data. With TCP, a lost packet on the display stream would block input delivery (head-of-line blocking). QUIC's independent streams eliminate this -- each data type flows independently.
|
||||
|
||||
### Built-in Encryption
|
||||
QUIC mandates TLS 1.3. Every WayRay connection is encrypted by default, with no option to disable it. This is security by design, not by configuration.
|
||||
|
||||
### 0-RTT Reconnection
|
||||
When a client reconnects to a server it has connected to before, QUIC can resume the connection in zero round trips. This is critical for session mobility -- when you move your token to a new terminal, the reconnection must be fast.
|
||||
|
||||
### Connection Migration
|
||||
If a client's IP address changes (e.g., switching from WiFi to Ethernet), QUIC can migrate the connection without dropping it. The session continues seamlessly.
|
||||
|
||||
## Stream Layout
|
||||
|
||||
| Stream | Purpose | Direction | Reliability |
|
||||
|--------|---------|-----------|------------|
|
||||
| 0 | Control | Bidirectional | Reliable |
|
||||
| 1 | Display | Server -> Client | Semi-reliable* |
|
||||
| 2 | Input | Client -> Server | Reliable |
|
||||
| 3 | Audio | Bidirectional | Semi-reliable* |
|
||||
| 4+ | USB | Bidirectional | Reliable |
|
||||
|
||||
*Semi-reliable: uses short-lived streams or datagrams so that old data is dropped rather than blocking new data.
|
||||
|
||||
## Priority
|
||||
|
||||
Input has the highest priority. A keystroke must reach the server even if a large display frame is in transit. QUIC's flow control and WayRay's priority scheme ensure input events are never delayed by display data.
|
||||
|
||||
## Firewall Considerations
|
||||
|
||||
QUIC runs over UDP. Some corporate firewalls block non-standard UDP traffic. If you encounter connectivity issues:
|
||||
|
||||
1. Ensure UDP port 4433 (default) is open between client and server
|
||||
2. If UDP is blocked entirely, a future version of WayRay will support TCP fallback via WebSocket tunneling
|
||||
79
book/src/concepts/pluggable-wm.md
Normal file
79
book/src/concepts/pluggable-wm.md
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# Pluggable Window Management
|
||||
|
||||
One of the things Unix enthusiasts loved about the SunRay + Solaris stack was X11's clean separation of display server and window manager. You could run CDE, FVWM, dwm, i3, or anything else -- the display server didn't care. Wayland merged these roles into one monolithic compositor, killing that flexibility.
|
||||
|
||||
WayRay brings it back.
|
||||
|
||||
## The Design
|
||||
|
||||
WayRay separates the **compositor** (rendering, protocol handling, input dispatch, frame encoding) from the **window manager** (layout, focus policy, keybindings, decorations). They are separate processes communicating via a custom Wayland protocol.
|
||||
|
||||
```
|
||||
┌─────────────────────────┐ Wayland Protocol ┌───────────────────┐
|
||||
│ WayRay Compositor │◄────────────────────────►│ Window Manager │
|
||||
│ │ │ │
|
||||
│ Renders frames │ "Here are the windows" │ Decides layout │
|
||||
│ Handles Wayland clients │ ─────────────────────► │ Sets focus │
|
||||
│ Encodes for network │ │ Binds keys │
|
||||
│ Manages sessions │ "Put them here" │ Manages workspaces│
|
||||
│ │ ◄───────────────────── │ │
|
||||
└─────────────────────────┘ └───────────────────┘
|
||||
```
|
||||
|
||||
## Two-Phase Transactions
|
||||
|
||||
Inspired by [River's window management protocol](https://isaacfreund.com/blog/river-window-management/), all WM operations happen in two atomic phases:
|
||||
|
||||
### Phase 1: Manage (Policy)
|
||||
When something changes (new window, resize request, fullscreen request), the compositor tells the WM. The WM responds with policy decisions: "make this window 800x600", "focus this one", "use server-side decorations". The compositor then sends configure events to the affected Wayland clients.
|
||||
|
||||
### Phase 2: Render (Visual)
|
||||
After clients acknowledge their new sizes and commit, the compositor tells the WM the final dimensions. The WM specifies exact visual placement: positions, z-order, borders. The compositor applies everything **atomically in one frame** -- no visual glitches, no half-rendered layouts.
|
||||
|
||||
## Supported Workflows
|
||||
|
||||
Because the WM is external, any paradigm is possible:
|
||||
|
||||
| Style | Description | Examples |
|
||||
|-------|-------------|---------|
|
||||
| **Floating** | Traditional desktop with draggable windows | Openbox, FVWM, Mutter |
|
||||
| **Tiling** | Windows automatically fill the screen | i3, dwm, bspwm |
|
||||
| **Dynamic** | Switch between tiling and floating on the fly | awesome, xmonad |
|
||||
| **Keyboard-driven** | Fullscreen windows, prefix-key navigation | ratpoison, StumpWM |
|
||||
| **Scrolling** | Windows in a strip, viewport scrolls | niri, PaperWM |
|
||||
| **Custom** | Write your own in any language | You! |
|
||||
|
||||
## Default WM
|
||||
|
||||
WayRay ships with a built-in floating WM that activates when no external WM is connected. It provides a comfortable default experience with basic keyboard shortcuts. When you connect an external WM, the built-in one steps aside.
|
||||
|
||||
## Writing a Custom WM
|
||||
|
||||
A window manager for WayRay is just a Wayland client. You can write one in any language with Wayland client bindings:
|
||||
|
||||
- **Rust** using wayland-client
|
||||
- **C** using libwayland-client
|
||||
- **Python** using pywayland
|
||||
- **Go** using go-wayland
|
||||
|
||||
The WM connects to the compositor, binds the `wayray_wm_manager_v1` global, and starts receiving window events. See the [Protocol Reference](../dev/protocol.md) for details.
|
||||
|
||||
## Crash Resilience
|
||||
|
||||
If your WM crashes:
|
||||
- The compositor continues running
|
||||
- All your applications stay alive
|
||||
- Windows freeze in their last positions
|
||||
- Restart the WM and it picks up where it left off
|
||||
|
||||
This is critical for a thin client server: a WM bug must never destroy user sessions.
|
||||
|
||||
## Hot-Swapping
|
||||
|
||||
You can switch WMs without restarting:
|
||||
1. Start a new WM process
|
||||
2. It connects and the old WM receives a "replaced" event
|
||||
3. The old WM disconnects
|
||||
4. The new WM receives the full window list and takes over
|
||||
|
||||
Try i3-style tiling in the morning, switch to floating for a presentation, back to tiling after lunch -- all without closing a single application.
|
||||
72
book/src/concepts/session-mobility.md
Normal file
72
book/src/concepts/session-mobility.md
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
# Session Mobility
|
||||
|
||||
Session mobility is WayRay's signature feature, inherited from SunRay's legendary hot-desking capability.
|
||||
|
||||
## The Concept
|
||||
|
||||
Your desktop session is not tied to a physical device. It's tied to a **token** -- a smart card, NFC tag, or software identifier. Wherever you present that token, your session appears.
|
||||
|
||||
## How It Works
|
||||
|
||||
### Starting a Session
|
||||
|
||||
1. You sit at any WayRay client terminal
|
||||
2. You present your token (insert smart card, tap NFC, or the client sends a stored software token)
|
||||
3. The client sends the token to the server
|
||||
4. The server checks: does a session for this token already exist?
|
||||
- **No**: Create a new session, authenticate the user (PAM login), start the desktop
|
||||
- **Yes**: Resume the existing session (skip to step 5)
|
||||
5. The session binds to your client. Your desktop appears.
|
||||
|
||||
### Moving Between Terminals
|
||||
|
||||
1. You're working at Terminal A with your token
|
||||
2. You remove your token (or press the disconnect shortcut)
|
||||
3. Your session **suspends**: the client disconnects, but all applications keep running on the server
|
||||
4. You walk to Terminal B and present your token
|
||||
5. The server finds your suspended session and **resumes** it on Terminal B
|
||||
6. Your desktop appears on Terminal B -- all windows, all applications, exact state
|
||||
|
||||
The entire reconnection happens in under 500 milliseconds on a LAN.
|
||||
|
||||
### Session Persistence
|
||||
|
||||
When you disconnect, your session enters the **Suspended** state:
|
||||
- All applications continue running
|
||||
- Window positions and states are preserved
|
||||
- No frames are rendered or transmitted (saves CPU)
|
||||
- The session stays alive for a configurable timeout (default: 24 hours)
|
||||
- After timeout, the session is destroyed and applications are terminated
|
||||
|
||||
## Token Types
|
||||
|
||||
WayRay supports pluggable token providers:
|
||||
|
||||
### Software Token (Default)
|
||||
A UUID stored on the client device. Simplest to set up, no hardware needed. Suitable for personal devices and development.
|
||||
|
||||
### Smart Card (PC/SC)
|
||||
A physical smart card with a unique ID. Insert to connect, remove to disconnect. Enterprise-grade, tamper-resistant. Requires a smart card reader on the client.
|
||||
|
||||
### NFC
|
||||
Tap a badge or phone to connect. Modern, fast, convenient. Requires an NFC reader on the client.
|
||||
|
||||
## Multi-Server Hot-Desking
|
||||
|
||||
In a multi-server deployment, your session might be running on Server A while you sit at a client connected to Server B:
|
||||
|
||||
1. Client connects to Server B with your token
|
||||
2. Server B checks its local session registry -- no match
|
||||
3. Server B queries the distributed session registry
|
||||
4. Registry says: this token's session is on Server A
|
||||
5. Server B redirects the client to Server A
|
||||
6. Client connects to Server A, session resumes
|
||||
|
||||
This works across buildings, campuses, or even geographic regions (with higher latency).
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- **Token theft**: A stolen smart card can access the session. Mitigate with PIN requirements or multi-factor authentication.
|
||||
- **Session timeout**: Sessions should not persist indefinitely. Configure timeouts appropriate to your security policy.
|
||||
- **Lock on disconnect**: Optionally lock the session screen when the token is removed, requiring password re-entry on resume.
|
||||
- **Simultaneous tokens**: If the same token appears on two clients simultaneously, the older connection is terminated (last-writer-wins).
|
||||
69
book/src/dev/building.md
Normal file
69
book/src/dev/building.md
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
# Building from Source
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Ensure you have:
|
||||
- Rust toolchain (latest stable, install via [rustup](https://rustup.rs))
|
||||
- System development libraries (see [Installation](../installation.md))
|
||||
|
||||
## Build Commands
|
||||
|
||||
```bash
|
||||
# Development build (faster compilation)
|
||||
cargo build
|
||||
|
||||
# Release build (optimized)
|
||||
cargo build --release
|
||||
|
||||
# Build only the server
|
||||
cargo build -p wayray-server
|
||||
|
||||
# Build only the client
|
||||
cargo build -p wayray-client
|
||||
|
||||
# Run tests
|
||||
cargo test --workspace
|
||||
|
||||
# Run with logging
|
||||
RUST_LOG=wayray=debug cargo run -p wayray-server
|
||||
```
|
||||
|
||||
## Feature Flags
|
||||
|
||||
### wayray-server
|
||||
|
||||
| Feature | Default | Description |
|
||||
|---------|---------|-------------|
|
||||
| `renderer-pixman` | yes | Software rendering (no GPU needed) |
|
||||
| `renderer-gles` | no | Hardware OpenGL ES rendering |
|
||||
| `xwayland` | no | X11 application support |
|
||||
| `vaapi` | no | Hardware video encoding |
|
||||
|
||||
### wayray-client
|
||||
|
||||
| Feature | Default | Description |
|
||||
|---------|---------|-------------|
|
||||
| `smartcard` | no | PC/SC smart card support |
|
||||
| `nfc` | no | NFC token support |
|
||||
|
||||
## Development Tips
|
||||
|
||||
### Running Nested (Recommended for Development)
|
||||
|
||||
During development, run the WayRay server nested inside your existing desktop using the Winit backend:
|
||||
|
||||
```bash
|
||||
cargo run -p wayray-server -- --backend winit
|
||||
```
|
||||
|
||||
This opens a window on your desktop that acts as the WayRay display. No need for a separate TTY.
|
||||
|
||||
### Running the Client Against a Local Server
|
||||
|
||||
```bash
|
||||
# Terminal 1: Start server
|
||||
cargo run -p wayray-server -- --backend winit --listen 127.0.0.1:4433
|
||||
|
||||
# Terminal 2: Start client
|
||||
cargo run -p wayray-client -- --server 127.0.0.1:4433 --token dev
|
||||
```
|
||||
31
book/src/dev/contributing.md
Normal file
31
book/src/dev/contributing.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# Contributing
|
||||
|
||||
## Development Setup
|
||||
|
||||
1. Clone the repository
|
||||
2. Install prerequisites (see [Building from Source](./building.md))
|
||||
3. Run `cargo build` to verify everything compiles
|
||||
4. Run `cargo test --workspace` to verify tests pass
|
||||
|
||||
## Code Style
|
||||
|
||||
- Follow standard Rust conventions (`cargo fmt`, `cargo clippy`)
|
||||
- Use `miette` for user-facing error messages with helpful diagnostics
|
||||
- Use `tracing` for logging (not `println!` or `log`)
|
||||
- Use `SeaORM` for any database operations (never raw SQL)
|
||||
- Write tests for new functionality
|
||||
|
||||
## Architecture Guidelines
|
||||
|
||||
- Keep the client as simple as possible -- it's a "dumb terminal"
|
||||
- Server-side complexity is preferred over client-side complexity
|
||||
- Protocol changes must update both server and client in the same PR
|
||||
- Performance matters: benchmark before and after for encoding/networking changes
|
||||
|
||||
## Pull Request Process
|
||||
|
||||
1. Create a feature branch from `main`
|
||||
2. Make your changes with descriptive commits
|
||||
3. Ensure `cargo test --workspace` passes
|
||||
4. Ensure `cargo clippy --workspace` is clean
|
||||
5. Open a PR with a description of what and why
|
||||
53
book/src/dev/protocol.md
Normal file
53
book/src/dev/protocol.md
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# Protocol Reference
|
||||
|
||||
For the complete wire protocol specification, see [docs/protocols/wayray-wire-protocol.md](../../../docs/protocols/wayray-wire-protocol.md) in the repository.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Message Flow: Connection
|
||||
|
||||
```
|
||||
Client Server
|
||||
│ │
|
||||
│──── QUIC Connect ────────────►│
|
||||
│ │
|
||||
│──── ClientHello ─────────────►│
|
||||
│ (token, capabilities) │
|
||||
│ │
|
||||
│◄──── ServerHello ─────────────│
|
||||
│ (session, keymap) │
|
||||
│ │
|
||||
│◄──── FrameUpdate ────────────│ (display stream starts)
|
||||
│──── InputEvent ──────────────►│ (input stream starts)
|
||||
│◄───► AudioFrame ─────────────►│ (audio stream starts)
|
||||
```
|
||||
|
||||
### Message Flow: Hot-Desking
|
||||
|
||||
```
|
||||
Client A Server Client B
|
||||
│ │ │
|
||||
│ (connected, active) │ │
|
||||
│ │ │
|
||||
│── Token Remove ──────►│ │
|
||||
│ │ Session suspends │
|
||||
│ (disconnected) │ │
|
||||
│ │ │
|
||||
│ │◄── ClientHello ────│
|
||||
│ │ (same token) │
|
||||
│ │ │
|
||||
│ │── ServerHello ────►│
|
||||
│ │ (Resumed) │
|
||||
│ │ │
|
||||
│ │── FrameUpdate ───►│
|
||||
```
|
||||
|
||||
### Encoding Types
|
||||
|
||||
| Type | Use Case | Quality | Bandwidth |
|
||||
|------|----------|---------|-----------|
|
||||
| `Zstd` | Text, UI, small changes | Lossless | Low |
|
||||
| `Jpeg` | Photos, images | Lossy | Medium |
|
||||
| `H264` | Video, animations | Lossy | Medium-High |
|
||||
| `Av1` | Video (better compression) | Lossy | Low-Medium |
|
||||
| `Raw` | Fallback, tiny regions | Lossless | High |
|
||||
57
book/src/installation.md
Normal file
57
book/src/installation.md
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
# Installation
|
||||
|
||||
## Building from Source
|
||||
|
||||
### Prerequisites
|
||||
|
||||
**Server:**
|
||||
- Rust (latest stable)
|
||||
- Linux with Wayland support
|
||||
- Development libraries:
|
||||
```bash
|
||||
# Debian/Ubuntu
|
||||
sudo apt install libwayland-dev libinput-dev libudev-dev libgbm-dev \
|
||||
libxkbcommon-dev libpixman-1-dev libseat-dev libpipewire-0.3-dev \
|
||||
libssl-dev pkg-config cmake
|
||||
|
||||
# Fedora
|
||||
sudo dnf install wayland-devel libinput-devel systemd-devel mesa-libgbm-devel \
|
||||
libxkbcommon-devel pixman-devel libseat-devel pipewire-devel \
|
||||
openssl-devel pkg-config cmake
|
||||
```
|
||||
|
||||
**Client:**
|
||||
- Rust (latest stable)
|
||||
- GPU drivers (Vulkan recommended for wgpu)
|
||||
- Development libraries:
|
||||
```bash
|
||||
# Debian/Ubuntu
|
||||
sudo apt install libxkbcommon-dev libssl-dev pkg-config
|
||||
|
||||
# Fedora
|
||||
sudo dnf install libxkbcommon-devel openssl-devel pkg-config
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/wayray/wayray.git
|
||||
cd wayray
|
||||
|
||||
# Build all components
|
||||
cargo build --release
|
||||
|
||||
# Binaries are in target/release/
|
||||
ls target/release/wayray-server target/release/wayray-client target/release/wayray-ctl
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
```dockerfile
|
||||
# Server image available
|
||||
docker pull wayray/wayray-server:latest
|
||||
|
||||
# Or build locally
|
||||
docker build -t wayray-server -f docker/server.Dockerfile .
|
||||
```
|
||||
41
book/src/introduction.md
Normal file
41
book/src/introduction.md
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# WayRay
|
||||
|
||||
**WayRay** is a modern thin client Wayland compositor inspired by Oracle/Sun's legendary SunRay system. It brings stateless thin client computing into the Wayland era with session mobility, hot-desking, and zero-state endpoints.
|
||||
|
||||
## What is a Thin Client Compositor?
|
||||
|
||||
In a traditional desktop, applications run on the machine in front of you. In a thin client system, applications run on a remote server, and only the display output is transmitted to your device. Your local device (the "thin client") is just a screen, keyboard, and mouse.
|
||||
|
||||
WayRay takes this further by being a **Wayland compositor** on the server side. Applications connect to WayRay via the standard Wayland protocol, completely unaware they're being remoted. WayRay renders their output, encodes it efficiently, and transmits it to the client over the network.
|
||||
|
||||
## Inspired by SunRay
|
||||
|
||||
Sun Microsystems introduced SunRay in 1999 as a truly stateless thin client. Its defining feature was **session mobility**: users carried a smart card, and wherever they inserted it, their desktop appeared -- all windows, all applications, exact state. Pull the card, walk to another terminal, insert it, and your desktop followed in under a second.
|
||||
|
||||
SunRay was discontinued in 2014 when Oracle deprioritized the product line. WayRay aims to bring back these concepts with modern technology:
|
||||
|
||||
| SunRay | WayRay |
|
||||
|--------|--------|
|
||||
| Proprietary ALP protocol | QUIC with TLS 1.3 |
|
||||
| X11 server (Xnewt) | Wayland compositor (Smithay) |
|
||||
| Purpose-built hardware | Any device |
|
||||
| Smart card only | Smart card, NFC, or software tokens |
|
||||
| Solaris/Linux server | Linux server |
|
||||
|
||||
## Key Features
|
||||
|
||||
- **Session Mobility**: Your desktop follows your token, not your device
|
||||
- **Stateless Clients**: Nothing stored on the client device
|
||||
- **Wayland Native**: Standard Wayland applications work unmodified
|
||||
- **Adaptive Encoding**: Lossless text, lossy video, content-aware compression
|
||||
- **Low Latency**: QUIC transport with priority-based stream multiplexing
|
||||
- **Audio & USB**: Full audio forwarding and USB device redirection
|
||||
- **Secure by Default**: TLS 1.3 mandatory, no data at rest on clients
|
||||
|
||||
## Who is WayRay For?
|
||||
|
||||
- **Enterprise IT**: Centralized desktop management, zero-trust endpoints
|
||||
- **Education**: Shared lab computers where any student sits anywhere
|
||||
- **Government/Defense**: Stateless endpoints with no data exfiltration risk
|
||||
- **Remote Work**: Access your full desktop from any location
|
||||
- **Kiosk/Public Terminals**: Purpose-built stateless access points
|
||||
65
book/src/overview.md
Normal file
65
book/src/overview.md
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# Overview
|
||||
|
||||
## System Components
|
||||
|
||||
WayRay consists of four components:
|
||||
|
||||
### wayray-server
|
||||
|
||||
The server is a Wayland compositor built on [Smithay](https://github.com/Smithay/smithay). It:
|
||||
|
||||
- Accepts connections from Wayland applications (terminals, browsers, editors, etc.)
|
||||
- Manages windows, input focus, and display outputs
|
||||
- Renders all application surfaces into a framebuffer
|
||||
- Encodes changed regions and transmits them to connected clients
|
||||
- Manages user sessions and authentication
|
||||
- Forwards audio via PipeWire
|
||||
|
||||
The server runs on Linux and can operate headless (no GPU required) or with GPU acceleration.
|
||||
|
||||
### wayray-client
|
||||
|
||||
The client is a lightweight viewer application that:
|
||||
|
||||
- Connects to a WayRay server over QUIC
|
||||
- Receives and decodes display frames
|
||||
- Renders frames to the local display
|
||||
- Captures keyboard, mouse, and touch input and sends it to the server
|
||||
- Handles audio playback and microphone capture
|
||||
- Manages USB device forwarding
|
||||
|
||||
The client runs on Linux, with macOS and Windows support planned.
|
||||
|
||||
### wayray-protocol
|
||||
|
||||
A shared library defining the wire protocol between server and client. This ensures both sides agree on message formats at compile time.
|
||||
|
||||
### wayray-ctl
|
||||
|
||||
A command-line tool for administering WayRay servers:
|
||||
|
||||
- List and manage active sessions
|
||||
- Configure server settings
|
||||
- Monitor performance and network statistics
|
||||
- Manage multi-server groups
|
||||
|
||||
## How It Works
|
||||
|
||||
```
|
||||
You sit at a WayRay client terminal.
|
||||
You insert your token (smart card, tap NFC, or open software token).
|
||||
The client sends your token to the server.
|
||||
The server finds or creates your session.
|
||||
Your desktop appears on screen -- all your applications running, exactly as you left them.
|
||||
You work normally. Every keystroke goes to the server; every frame comes back.
|
||||
You pull your token and walk away. Your session suspends but keeps running.
|
||||
You sit at another terminal across the building. Insert your token.
|
||||
Your desktop reappears. Same windows, same state. Under a second.
|
||||
```
|
||||
|
||||
## Network Requirements
|
||||
|
||||
- **LAN**: 100 Mbps minimum, Gigabit recommended
|
||||
- **WAN**: 5 Mbps minimum for basic use, 20+ Mbps for video content
|
||||
- **Latency**: < 30ms for comfortable interactive use, < 10ms ideal
|
||||
- **Protocol**: QUIC over UDP (port configurable, default 4433)
|
||||
69
book/src/quickstart.md
Normal file
69
book/src/quickstart.md
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
# Quick Start
|
||||
|
||||
This guide gets you running a WayRay server and connecting a client in minutes.
|
||||
|
||||
## 1. Generate TLS Certificates
|
||||
|
||||
WayRay requires TLS for all connections. For development, generate self-signed certificates:
|
||||
|
||||
```bash
|
||||
wayray-ctl cert generate --output ./certs
|
||||
```
|
||||
|
||||
This creates `certs/server.crt` and `certs/server.key`.
|
||||
|
||||
## 2. Start the Server
|
||||
|
||||
```bash
|
||||
wayray-server \
|
||||
--cert ./certs/server.crt \
|
||||
--key ./certs/server.key \
|
||||
--listen 0.0.0.0:4433 \
|
||||
--renderer pixman # Use 'gles' if GPU is available
|
||||
```
|
||||
|
||||
The server starts and listens for client connections on port 4433.
|
||||
|
||||
## 3. Connect a Client
|
||||
|
||||
On another machine (or the same machine for testing):
|
||||
|
||||
```bash
|
||||
wayray-client \
|
||||
--server 192.168.1.100:4433 \
|
||||
--token my-dev-token \
|
||||
--ca ./certs/server.crt # Trust the self-signed cert
|
||||
```
|
||||
|
||||
A window opens showing your remote desktop. Open applications on the server and they appear in the client.
|
||||
|
||||
## 4. Launch Applications
|
||||
|
||||
From an SSH session to the server, or from a terminal within the WayRay session:
|
||||
|
||||
```bash
|
||||
# Set the Wayland display to your WayRay session
|
||||
export WAYLAND_DISPLAY=wayray-0
|
||||
|
||||
# Launch applications
|
||||
foot & # Terminal
|
||||
firefox & # Browser
|
||||
nautilus & # File manager
|
||||
```
|
||||
|
||||
## 5. Test Session Mobility
|
||||
|
||||
1. Note the token you used (`my-dev-token`)
|
||||
2. Close the client window (or press the disconnect key)
|
||||
3. Your applications keep running on the server
|
||||
4. Reconnect with the same token:
|
||||
```bash
|
||||
wayray-client --server 192.168.1.100:4433 --token my-dev-token
|
||||
```
|
||||
5. Your desktop reappears with all windows intact
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Server Configuration](./server/configuration.md) for production setup
|
||||
- [Token Setup](./client/tokens.md) for smart card or NFC tokens
|
||||
- [Session Mobility](./concepts/session-mobility.md) to understand hot-desking
|
||||
35
book/src/server/configuration.md
Normal file
35
book/src/server/configuration.md
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# Server Configuration
|
||||
|
||||
> This page will be written as the server implementation matures.
|
||||
|
||||
## Configuration File
|
||||
|
||||
WayRay server reads configuration from `wayray-server.toml`:
|
||||
|
||||
```toml
|
||||
[server]
|
||||
listen = "0.0.0.0:4433"
|
||||
renderer = "pixman" # or "gles"
|
||||
|
||||
[tls]
|
||||
cert = "/etc/wayray/server.crt"
|
||||
key = "/etc/wayray/server.key"
|
||||
|
||||
[session]
|
||||
timeout = "24h" # How long suspended sessions persist
|
||||
max_sessions = 100 # Maximum concurrent sessions
|
||||
|
||||
[encoding]
|
||||
strategy = "adaptive" # "lossless", "lossy", or "adaptive"
|
||||
max_fps = 60
|
||||
tile_size = 64
|
||||
|
||||
[audio]
|
||||
enabled = true
|
||||
codec = "opus"
|
||||
sample_rate = 48000
|
||||
|
||||
[usb]
|
||||
enabled = true
|
||||
allowed_classes = ["mass-storage", "printer"]
|
||||
```
|
||||
7
book/src/server/multi-server.md
Normal file
7
book/src/server/multi-server.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Multi-Server Setup
|
||||
|
||||
> This page will be written when multi-server support is implemented.
|
||||
|
||||
## Overview
|
||||
|
||||
For high availability and scalability, WayRay can run in a multi-server configuration where sessions are distributed across servers and users can hot-desk between any terminal connected to any server in the group.
|
||||
37
book/src/server/running.md
Normal file
37
book/src/server/running.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# Running the Server
|
||||
|
||||
> This page will be expanded as the server implementation matures.
|
||||
|
||||
## Standalone
|
||||
|
||||
```bash
|
||||
wayray-server --config /etc/wayray/wayray-server.toml
|
||||
```
|
||||
|
||||
## Systemd Service
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=WayRay Thin Client Server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/wayray-server --config /etc/wayray/wayray-server.toml
|
||||
Restart=always
|
||||
User=wayray
|
||||
Group=wayray
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
## Docker
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name wayray-server \
|
||||
-p 4433:4433/udp \
|
||||
-v /etc/wayray:/etc/wayray:ro \
|
||||
wayray/wayray-server:latest
|
||||
```
|
||||
26
book/src/server/sessions.md
Normal file
26
book/src/server/sessions.md
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# Session Management
|
||||
|
||||
> This page will be expanded as session management is implemented.
|
||||
|
||||
## Session States
|
||||
|
||||
- **Creating**: New session being initialized
|
||||
- **Active**: Session connected to a client, rendering and transmitting
|
||||
- **Suspended**: Client disconnected, applications still running, no rendering
|
||||
- **Destroyed**: Session terminated, all applications closed
|
||||
|
||||
## Managing Sessions
|
||||
|
||||
```bash
|
||||
# List active sessions
|
||||
wayray-ctl session list
|
||||
|
||||
# View session details
|
||||
wayray-ctl session info <session-id>
|
||||
|
||||
# Terminate a session
|
||||
wayray-ctl session kill <session-id>
|
||||
|
||||
# Set session timeout
|
||||
wayray-ctl session set-timeout <session-id> 48h
|
||||
```
|
||||
46
docs/ai/adr/001-compositor-framework.md
Normal file
46
docs/ai/adr/001-compositor-framework.md
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# ADR-001: Compositor Framework Selection
|
||||
|
||||
## Status
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
We need a Wayland compositor library to build WayRay's server-side compositor. The compositor must support headless operation (no physical display required), framebuffer capture for network transmission, and input injection from remote clients.
|
||||
|
||||
## Options Considered
|
||||
|
||||
### 1. Smithay (Rust)
|
||||
- Purpose-built Rust library for Wayland compositors
|
||||
- Provides backend abstractions (DRM, libinput, EGL), Wayland protocol helpers, and rendering infrastructure
|
||||
- Used by production compositors: cosmic-comp (System76), niri
|
||||
- Active development, strong community
|
||||
- Supports PixmanRenderer (software, headless) and GlesRenderer (hardware)
|
||||
- ExportMem trait allows framebuffer readback
|
||||
|
||||
### 2. wlroots (C)
|
||||
- Mature C library used by Sway, Hyprland, and many others
|
||||
- Would require FFI bindings, losing Rust safety guarantees
|
||||
- Opinionated about rendering pipeline; harder to customize for remote display
|
||||
- More battle-tested but less idiomatic for a Rust project
|
||||
|
||||
### 3. From scratch
|
||||
- Maximum flexibility but enormous implementation effort
|
||||
- Would need to implement Wayland protocol, DRM/KMS, input handling manually
|
||||
- Not justified when Smithay provides these building blocks
|
||||
|
||||
## Decision
|
||||
**Use Smithay** as the compositor framework.
|
||||
|
||||
## Rationale
|
||||
- Native Rust with full type safety and ownership semantics
|
||||
- Modular architecture: we use only what we need
|
||||
- PixmanRenderer enables headless server operation without GPU
|
||||
- ExportMem trait provides the framebuffer capture path we need
|
||||
- OutputDamageTracker gives us efficient dirty region tracking
|
||||
- calloop event loop integrates cleanly with our networking layer
|
||||
- Production-proven by cosmic-comp and niri
|
||||
- Active maintenance and responsive community
|
||||
|
||||
## Consequences
|
||||
- We inherit Smithay's API design and must follow its handler trait pattern
|
||||
- We depend on Smithay's release cycle for protocol support
|
||||
- calloop (not tokio) is our event loop, which affects how we integrate async networking
|
||||
66
docs/ai/adr/002-network-transport.md
Normal file
66
docs/ai/adr/002-network-transport.md
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# ADR-002: Network Transport Protocol
|
||||
|
||||
## Status
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
WayRay must transmit display frames, input events, audio, and USB data between server and client with low latency. The transport must handle multiple independent data streams with different priority and reliability requirements.
|
||||
|
||||
## Options Considered
|
||||
|
||||
### 1. TCP (multiple connections)
|
||||
- Reliable, ordered delivery
|
||||
- Head-of-line blocking: a lost display frame packet blocks all subsequent frames
|
||||
- No built-in multiplexing; need multiple connections or application-layer framing
|
||||
- Well-understood, simple implementation
|
||||
|
||||
### 2. UDP (custom protocol)
|
||||
- No head-of-line blocking
|
||||
- Must implement reliability, ordering, and congestion control ourselves
|
||||
- Maximum flexibility but significant implementation effort
|
||||
- What SunRay's ALP did (UDP for display/input, TCP for control)
|
||||
|
||||
### 3. QUIC (via quinn)
|
||||
- Built on UDP with TLS 1.3 encryption mandatory
|
||||
- Independent streams: loss on stream 1 doesn't block stream 2
|
||||
- 0-RTT connection resumption for fast session reconnection
|
||||
- Built-in congestion control and flow control
|
||||
- Multiplexing eliminates need for multiple connections
|
||||
- Connection migration: survives IP address changes (WiFi -> Ethernet)
|
||||
- quinn crate is mature and well-maintained in Rust
|
||||
|
||||
### 4. WebRTC
|
||||
- Designed for peer-to-peer media streaming
|
||||
- Overkill for our client-server architecture
|
||||
- Complex negotiation (ICE, STUN, TURN)
|
||||
- Good codec support but unnecessary complexity
|
||||
|
||||
## Decision
|
||||
**Use QUIC via the quinn crate** as the primary transport protocol.
|
||||
|
||||
## Stream Architecture
|
||||
|
||||
| Stream ID | Channel | Reliability | Priority | Direction |
|
||||
|-----------|---------|------------|----------|-----------|
|
||||
| 0 | Control | Reliable | High | Bidirectional |
|
||||
| 1 | Display | Unreliable* | High | Server -> Client |
|
||||
| 2 | Input | Reliable | Highest | Client -> Server |
|
||||
| 3 | Audio | Unreliable* | Medium | Bidirectional |
|
||||
| 4+ | USB | Reliable | Low | Bidirectional |
|
||||
|
||||
*Unreliable streams use QUIC datagrams or short-lived unidirectional streams where old frames are discarded rather than retransmitted.
|
||||
|
||||
## Rationale
|
||||
- Stream multiplexing maps naturally to our independent channels (display, input, audio, USB)
|
||||
- No head-of-line blocking between channels: a lost display frame doesn't delay input
|
||||
- 0-RTT reconnection enables sub-500ms session resumption for hot-desking
|
||||
- Connection migration supports client network changes without session interruption
|
||||
- TLS 1.3 mandatory means encryption is built-in, not an afterthought
|
||||
- quinn is production-quality Rust QUIC implementation
|
||||
- Avoids reinventing reliability/congestion control on raw UDP
|
||||
|
||||
## Consequences
|
||||
- Requires TLS certificate management (self-signed for development, proper PKI for production)
|
||||
- QUIC is newer than TCP; some corporate firewalls may block UDP
|
||||
- Mitigation: fallback to TCP with WebSocket tunneling as future work
|
||||
- quinn is async (tokio), while Smithay uses calloop; need bridging between the two event loops
|
||||
93
docs/ai/adr/003-frame-encoding-strategy.md
Normal file
93
docs/ai/adr/003-frame-encoding-strategy.md
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
# ADR-003: Frame Encoding Strategy
|
||||
|
||||
## Status
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
WayRay must transmit rendered Wayland compositor output from server to client efficiently. The encoding must balance bandwidth, latency, and visual quality. Different regions of the display have different characteristics (static UI vs video vs text) that benefit from different encoding strategies.
|
||||
|
||||
## Options Considered
|
||||
|
||||
### 1. Full-frame video encoding (H.264/AV1 everything)
|
||||
- Simple pipeline: render -> encode entire frame -> transmit -> decode
|
||||
- Good for video-heavy workloads
|
||||
- Introduces compression artifacts on text and UI elements
|
||||
- Consistent latency characteristics
|
||||
- Hardware encoding available (VAAPI, NVENC)
|
||||
|
||||
### 2. Differential lossless (like waypipe)
|
||||
- XOR diff against previous frame, compress with zstd/lz4
|
||||
- Perfect visual quality
|
||||
- Very efficient for mostly-static displays
|
||||
- Bandwidth spikes when large areas change
|
||||
- No hardware acceleration
|
||||
|
||||
### 3. Content-adaptive hybrid (like SPICE)
|
||||
- Heuristic classification of regions: text, UI, video, image
|
||||
- Each region encoded with optimal codec
|
||||
- Best quality-bandwidth tradeoff
|
||||
- Most complex to implement
|
||||
- SPICE proved this approach works at scale
|
||||
|
||||
### 4. Tile-based encoding
|
||||
- Divide frame into fixed-size tiles (e.g., 64x64)
|
||||
- Only encode tiles that changed (via damage tracking)
|
||||
- Each tile encoded independently (parallel-friendly)
|
||||
- Mix lossless and lossy per-tile based on content heuristics
|
||||
- Good balance of complexity and effectiveness
|
||||
|
||||
## Decision
|
||||
**Tile-based encoding with content-adaptive per-tile codec selection**, implemented progressively:
|
||||
|
||||
### Stage 1 (MVP): Differential lossless
|
||||
- Use OutputDamageTracker damage rectangles
|
||||
- XOR diff against previous frame for damaged regions
|
||||
- Compress with zstd
|
||||
- Good enough for development and LAN use
|
||||
|
||||
### Stage 2: Tile-based with lossy option
|
||||
- Divide into 64x64 tiles, only process damaged tiles
|
||||
- Lossless path: zstd-compressed diff (for text, UI)
|
||||
- Lossy path: JPEG/WebP for photographic content
|
||||
- Tile-level quality selection based on content entropy
|
||||
|
||||
### Stage 3: Hardware video encoding
|
||||
- Detect video regions (rapidly changing tiles)
|
||||
- Route video regions to H.264 encoder (VAAPI/NVENC)
|
||||
- Keep text/UI regions lossless
|
||||
- AV1 as alternative for better quality at same bitrate
|
||||
|
||||
## Frame Update Message Structure
|
||||
|
||||
```
|
||||
FrameUpdate {
|
||||
sequence: u64,
|
||||
timestamp: u64,
|
||||
full_width: u32,
|
||||
full_height: u32,
|
||||
regions: Vec<EncodedRegion>,
|
||||
}
|
||||
|
||||
EncodedRegion {
|
||||
x: u32,
|
||||
y: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
encoding: Encoding, // Zstd, Jpeg, H264, Raw
|
||||
data: Vec<u8>,
|
||||
}
|
||||
```
|
||||
|
||||
## Rationale
|
||||
- Progressive approach: ship MVP quickly, optimize later
|
||||
- Damage tracking from Smithay gives us changed regions for free
|
||||
- Tile-based approach is parallelizable (encode tiles on multiple cores)
|
||||
- Content-adaptive encoding avoids the "text looks blurry" problem of pure video encoding
|
||||
- Hardware encoding path available when needed for video content
|
||||
- Lossless path ensures perfect text rendering (critical for terminal/code editors)
|
||||
|
||||
## Consequences
|
||||
- Stage 1 is bandwidth-hungry on large screen changes (acceptable for LAN)
|
||||
- Need heuristics for tile content classification (can start simple: entropy-based)
|
||||
- Hardware encoding adds dependency on system GPU/driver support
|
||||
- Client must handle mixed encodings within a single frame update
|
||||
108
docs/ai/adr/004-session-management.md
Normal file
108
docs/ai/adr/004-session-management.md
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
# ADR-004: Session Management and Hot-Desking
|
||||
|
||||
## Status
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
SunRay's defining feature was session mobility: users could pull their smart card, walk to any terminal, insert the card, and their entire desktop appeared in under a second. WayRay must replicate this capability with modern technology.
|
||||
|
||||
## Design
|
||||
|
||||
### Session Lifecycle
|
||||
|
||||
```
|
||||
token insert
|
||||
[No Session] ───────────────> [Creating]
|
||||
│
|
||||
v
|
||||
┌──────────> [Active] <──────────┐
|
||||
│ │ │
|
||||
│ token remove │ token insert │
|
||||
│ v (new client)│
|
||||
│ [Suspended] ──────────┘
|
||||
│ │
|
||||
│ timeout │
|
||||
│ v
|
||||
│ [Destroyed]
|
||||
│
|
||||
│ client reconnect
|
||||
└──── (same token)
|
||||
```
|
||||
|
||||
### Session Identity
|
||||
- Each session is bound to a **token** (smart card ID, badge ID, or software token)
|
||||
- The token is NOT the user's identity -- it's a session locator
|
||||
- Authentication happens separately via PAM after session attachment
|
||||
- Multiple tokens can map to the same user (e.g., badge + phone NFC)
|
||||
|
||||
### Session Storage
|
||||
- Active session state: in-memory (fast lookup)
|
||||
- Session metadata (token mappings, user associations): SQLite via SeaORM
|
||||
- Session registry for multi-server: distributed key-value store (etcd or custom)
|
||||
|
||||
### Hot-Desking Flow
|
||||
|
||||
1. Client connects to any WayRay server with a token
|
||||
2. Server checks local session registry for token
|
||||
3. If not found locally, queries other servers in the group
|
||||
4. If session found on another server:
|
||||
- Source server suspends the session (detaches from old client)
|
||||
- Source server provides session endpoint to requesting server
|
||||
- Client is redirected to the source server
|
||||
- Session resumes on source server with new client
|
||||
5. If no session exists: create new session, authenticate user via PAM
|
||||
6. Entire flow target: < 500ms
|
||||
|
||||
### Session Suspension
|
||||
When a token is removed or client disconnects:
|
||||
- Compositor continues running (all applications stay alive)
|
||||
- Frame rendering pauses (no ExportMem, save CPU)
|
||||
- Network streams close gracefully
|
||||
- Session enters Suspended state with configurable timeout (default: 24 hours)
|
||||
- After timeout: session destroyed, all applications terminated
|
||||
|
||||
## Options Considered for Token Mechanism
|
||||
|
||||
### 1. Smart Card (PC/SC)
|
||||
- Direct SunRay heritage
|
||||
- Requires hardware on client (smart card reader)
|
||||
- Well-suited for enterprise/government environments
|
||||
- Libraries: pcsc-rust crate
|
||||
|
||||
### 2. NFC / RFID
|
||||
- Modern equivalent of smart card
|
||||
- Phone-based NFC as software token
|
||||
- Lower-friction than smart card insertion
|
||||
|
||||
### 3. Software Token
|
||||
- UUID-based token stored on client device
|
||||
- No hardware required
|
||||
- Can be combined with TOTP/FIDO2 for security
|
||||
- Most accessible for development and lightweight deployments
|
||||
|
||||
### 4. All of the above (pluggable)
|
||||
- Token is an opaque identifier; the mechanism doesn't matter to session management
|
||||
- Different authentication backends provide tokens via a trait
|
||||
|
||||
## Decision
|
||||
**Pluggable token system** with software tokens as the default, smart card and NFC as optional backends.
|
||||
|
||||
```rust
|
||||
trait TokenProvider {
|
||||
fn token_inserted(&self) -> Option<SessionToken>;
|
||||
fn token_removed(&self) -> bool;
|
||||
fn watch(&self) -> TokenEventStream;
|
||||
}
|
||||
```
|
||||
|
||||
## Rationale
|
||||
- Session mobility is the core differentiator; it must work across arbitrary token types
|
||||
- Software tokens lower the barrier to entry (no hardware needed)
|
||||
- Smart card support honors the SunRay heritage for enterprise deployments
|
||||
- Pluggable design avoids committing to a single mechanism
|
||||
|
||||
## Consequences
|
||||
- Must handle race conditions: token inserted on two clients simultaneously
|
||||
- Session redirect adds latency; must be optimized carefully
|
||||
- Multi-server session registry is a distributed systems problem (consistency vs availability)
|
||||
- Software tokens are less secure than hardware tokens (can be copied); mitigate with device binding
|
||||
72
docs/ai/adr/005-rendering-strategy.md
Normal file
72
docs/ai/adr/005-rendering-strategy.md
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
# ADR-005: Rendering Strategy
|
||||
|
||||
## Status
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
The WayRay server compositor must render Wayland client surfaces into a framebuffer for network transmission. The server may or may not have a GPU. The rendering path must support efficient framebuffer capture.
|
||||
|
||||
## Options Considered
|
||||
|
||||
### 1. GlesRenderer only (GPU required)
|
||||
- Hardware-accelerated compositing
|
||||
- ExportMem via glReadPixels (GPU -> CPU readback)
|
||||
- Requires EGL/OpenGL ES 2.0 capable GPU on server
|
||||
- Can feed DMA-BUF directly to hardware video encoder (zero-copy)
|
||||
|
||||
### 2. PixmanRenderer only (CPU software rendering)
|
||||
- Pure software rendering, no GPU needed
|
||||
- ExportMem is essentially free (pixels already in CPU memory)
|
||||
- Lower throughput for complex scenes
|
||||
- Perfect for headless server deployments (cloud VMs, containers)
|
||||
|
||||
### 3. Dual renderer with runtime selection
|
||||
- PixmanRenderer as default/fallback
|
||||
- GlesRenderer when GPU is available
|
||||
- Configuration-driven or auto-detected
|
||||
- Covers both headless and GPU-equipped deployments
|
||||
|
||||
## Decision
|
||||
**Dual renderer with runtime selection.** PixmanRenderer as the default, GlesRenderer when a GPU is available and the admin opts in.
|
||||
|
||||
## Rationale
|
||||
- Many server deployments are headless VMs or containers without GPUs
|
||||
- PixmanRenderer gives us zero-overhead framebuffer capture (no GPU readback needed)
|
||||
- GlesRenderer + DMA-BUF export to VAAPI enables zero-copy hardware encoding
|
||||
- The Smithay Renderer trait makes both interchangeable
|
||||
- Niri and cosmic-comp demonstrate this dual-renderer pattern successfully
|
||||
|
||||
## Rendering Pipeline
|
||||
|
||||
### PixmanRenderer path (default)
|
||||
```
|
||||
Wayland clients commit surfaces
|
||||
-> PixmanRenderer composites all surfaces into output buffer
|
||||
-> OutputDamageTracker identifies changed regions
|
||||
-> copy_framebuffer() returns pixels (zero-copy, already in RAM)
|
||||
-> Frame encoder processes damaged regions
|
||||
-> Transmit to client
|
||||
```
|
||||
|
||||
### GlesRenderer path (GPU available)
|
||||
```
|
||||
Wayland clients commit surfaces (may use DMA-BUF)
|
||||
-> GlesRenderer composites via OpenGL ES
|
||||
-> OutputDamageTracker identifies changed regions
|
||||
-> Option A: copy_framebuffer() via glReadPixels -> CPU -> encode
|
||||
-> Option B: DMA-BUF export -> VAAPI encoder (zero-copy)
|
||||
-> Transmit to client
|
||||
```
|
||||
|
||||
## Frame Cadence
|
||||
- Render on damage (not fixed rate): only render when clients commit new content
|
||||
- Cap at client's display refresh rate (sent during capability exchange)
|
||||
- During suspension (no client connected): stop rendering entirely
|
||||
- Coalesce rapid commits: batch within ~4ms window to avoid excessive rendering
|
||||
|
||||
## Consequences
|
||||
- PixmanRenderer cannot handle GPU-only client buffers (DMA-BUF with no CPU-accessible format)
|
||||
- Mitigation: advertise only SHM formats when running with PixmanRenderer
|
||||
- glReadPixels is a synchronization point that stalls the GPU pipeline
|
||||
- Mitigation: use PBO (Pixel Buffer Objects) for async readback
|
||||
- Dual renderer adds code complexity; managed via Smithay's trait-based abstraction
|
||||
42
docs/ai/adr/006-audio-forwarding.md
Normal file
42
docs/ai/adr/006-audio-forwarding.md
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# ADR-006: Audio Forwarding
|
||||
|
||||
## Status
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
WayRay must forward audio bidirectionally between server and client. Server-side applications produce audio output; client-side microphones capture audio input.
|
||||
|
||||
## Decision
|
||||
**PipeWire for server-side audio capture/playback, Opus codec for transport, QUIC stream for delivery.**
|
||||
|
||||
## Design
|
||||
|
||||
### Server Side
|
||||
- Create a virtual PipeWire sink per session (applications route audio here)
|
||||
- Create a virtual PipeWire source per session (for microphone input from client)
|
||||
- Capture audio frames from the virtual sink
|
||||
- Encode to Opus (48kHz, stereo, 20ms frames for balance of latency and efficiency)
|
||||
- Transmit over dedicated QUIC audio stream
|
||||
|
||||
### Client Side
|
||||
- Receive Opus frames from QUIC audio stream
|
||||
- Decode with opus crate
|
||||
- Output to local audio device (via cpal or PipeWire client)
|
||||
- Capture local microphone input
|
||||
- Encode to Opus, send back over QUIC audio stream
|
||||
|
||||
### Synchronization
|
||||
- Audio frames carry timestamps aligned with display frame sequence numbers
|
||||
- Client performs adaptive jitter buffering (target: 20-60ms buffer)
|
||||
- Lip-sync: audio presented when corresponding display frame is shown
|
||||
|
||||
## Rationale
|
||||
- Opus is the gold standard for low-latency audio: 2.5ms minimum frame size, built-in FEC
|
||||
- PipeWire is the modern Linux audio stack, replacing PulseAudio
|
||||
- Dedicated QUIC stream prevents audio stuttering from display traffic
|
||||
- Per-session virtual devices provide proper isolation
|
||||
|
||||
## Consequences
|
||||
- PipeWire dependency on server (standard on modern Linux)
|
||||
- Opus adds ~5ms encoding latency + network RTT + ~5ms decoding
|
||||
- Must handle PipeWire session lifecycle (create/destroy with WayRay sessions)
|
||||
63
docs/ai/adr/007-project-structure.md
Normal file
63
docs/ai/adr/007-project-structure.md
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# ADR-007: Project Structure (Cargo Workspace)
|
||||
|
||||
## Status
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
WayRay consists of multiple binaries and shared libraries. We need a project structure that supports clean separation of concerns while sharing common code.
|
||||
|
||||
## Decision
|
||||
**Cargo workspace with the following crate layout:**
|
||||
|
||||
```
|
||||
wayray/
|
||||
├── Cargo.toml # Workspace root
|
||||
├── crates/
|
||||
│ ├── wayray-server/ # Wayland compositor + frame encoder + network server
|
||||
│ │ ├── Cargo.toml
|
||||
│ │ └── src/
|
||||
│ │ ├── main.rs
|
||||
│ │ ├── compositor/ # Smithay compositor implementation
|
||||
│ │ ├── encoder/ # Frame encoding pipeline
|
||||
│ │ ├── network/ # QUIC server, session management
|
||||
│ │ └── audio/ # PipeWire integration
|
||||
│ │
|
||||
│ ├── wayray-client/ # Remote viewer + input capture + decoder
|
||||
│ │ ├── Cargo.toml
|
||||
│ │ └── src/
|
||||
│ │ ├── main.rs
|
||||
│ │ ├── decoder/ # Frame decoding pipeline
|
||||
│ │ ├── renderer/ # Local display (wgpu/winit)
|
||||
│ │ ├── network/ # QUIC client
|
||||
│ │ ├── input/ # Input capture and forwarding
|
||||
│ │ └── audio/ # Local audio playback/capture
|
||||
│ │
|
||||
│ ├── wayray-protocol/ # Shared protocol definitions
|
||||
│ │ ├── Cargo.toml
|
||||
│ │ └── src/
|
||||
│ │ ├── lib.rs
|
||||
│ │ ├── messages.rs # Wire protocol message types
|
||||
│ │ ├── codec.rs # Serialization/deserialization
|
||||
│ │ └── version.rs # Protocol versioning
|
||||
│ │
|
||||
│ └── wayray-ctl/ # CLI management tool
|
||||
│ ├── Cargo.toml
|
||||
│ └── src/
|
||||
│ └── main.rs
|
||||
│
|
||||
├── docs/ # Documentation
|
||||
├── book/ # mdbook user guide
|
||||
└── tests/ # Integration tests
|
||||
```
|
||||
|
||||
## Rationale
|
||||
- `crates/` directory keeps workspace root clean
|
||||
- Protocol crate ensures server and client agree on wire format at compile time
|
||||
- Server and client have completely different dependency trees (smithay vs wgpu)
|
||||
- `wayray-ctl` as separate binary avoids bloating server/client with admin dependencies
|
||||
- Integration tests at workspace root can spin up server + client together
|
||||
|
||||
## Consequences
|
||||
- Must manage feature flags carefully across workspace
|
||||
- Protocol changes require updating both server and client
|
||||
- CI must build and test all crates
|
||||
79
docs/ai/adr/008-illumos-support.md
Normal file
79
docs/ai/adr/008-illumos-support.md
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# ADR-008: illumos as Primary Target Platform
|
||||
|
||||
## Status
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
WayRay is a spiritual successor to SunRay, which ran on Solaris. illumos (the open-source Solaris fork) is a natural target platform alongside Linux. However, the standard Wayland compositor stack (DRM/KMS, libinput, udev, libseat, PipeWire) is deeply Linux-specific.
|
||||
|
||||
## Research Findings
|
||||
|
||||
### What works on illumos
|
||||
- **Rust**: Tier 2 with host tools (`x86_64-unknown-illumos`), full std support
|
||||
- **tokio/mio**: Native event port support (`port_create`/`port_associate`), correct and performant
|
||||
- **quinn (QUIC)**: Pure Rust on tokio, fully portable
|
||||
- **rustls**: Pure Rust TLS, no platform dependencies
|
||||
- **Wayland protocol core**: Unix domain sockets + `SCM_RIGHTS` fd passing + `shm_open` all available
|
||||
- **PixmanRenderer**: Pure software, fully portable
|
||||
- **PulseAudio**: Available on illumos (default audio on OpenIndiana)
|
||||
|
||||
### What does NOT work on illumos
|
||||
- **DRM/KMS**: Only ancient Intel (pre-Gen8). AMD/modern Intel unsupported. Not needed for headless.
|
||||
- **libinput**: Depends on Linux evdev. illumos uses STREAMS-based `/dev/kbd` and `/dev/mouse`.
|
||||
- **udev**: Linux-specific. illumos uses `sysevent` for device notifications.
|
||||
- **libseat/logind**: Linux-specific session management. Irrelevant for headless compositor.
|
||||
- **PipeWire**: Not ported. PulseAudio available as alternative.
|
||||
- **memfd_create**: Linux-specific since kernel 3.17. Must fall back to `shm_open` + `mmap`.
|
||||
- **GBM**: Mesa's buffer manager, Linux-specific. Not needed without DRM.
|
||||
- **USB/IP**: Linux kernel module, no illumos equivalent.
|
||||
|
||||
### Precedent
|
||||
**cocoa-way** (macOS Wayland compositor on Smithay) proves the architecture: use Smithay with `default-features = false`, software rendering, custom input backend, no DRM/libinput/udev.
|
||||
|
||||
## Decision
|
||||
**Target illumos as a first-class platform via headless-first compositor architecture.**
|
||||
|
||||
WayRay's server is inherently headless -- it renders to a virtual framebuffer for network transmission, not to a physical display. This means the Linux-specific backends (DRM, libinput, udev, libseat) are optional features for local display, not core requirements.
|
||||
|
||||
### Platform Abstraction Strategy
|
||||
|
||||
```
|
||||
smithay (default-features = false)
|
||||
+ wayland_frontend (portable: Unix sockets + shm)
|
||||
+ renderer_pixman (portable: pure software)
|
||||
+ renderer_gl (optional: when EGL available)
|
||||
+ desktop (portable: Space, Window, helpers)
|
||||
+ xwayland (portable: X11 socket protocol)
|
||||
|
||||
Platform-specific features (feature flags):
|
||||
+ backend_drm (Linux only)
|
||||
+ backend_libinput (Linux only)
|
||||
+ backend_udev (Linux only)
|
||||
+ backend_session_libseat (Linux only)
|
||||
+ backend_winit (Linux + illumos dev mode)
|
||||
```
|
||||
|
||||
### Required Portability Work
|
||||
|
||||
| Component | Approach |
|
||||
|-----------|----------|
|
||||
| Shared memory | `shm_open` fallback when `memfd_create` unavailable |
|
||||
| Input (server-side) | Network input from clients (primary), `/dev/kbd`+`/dev/mouse` (illumos local) |
|
||||
| Audio | Trait-based backend: PipeWire (Linux), PulseAudio (illumos/Linux) |
|
||||
| Device hotplug | `sysevent` on illumos, `udev` on Linux |
|
||||
| USB forwarding | Userspace protocol over QUIC (not kernel USB/IP) |
|
||||
|
||||
## Rationale
|
||||
- SunRay ran on Solaris; WayRay running on illumos honors that heritage
|
||||
- Headless-first architecture makes illumos support natural, not an afterthought
|
||||
- The critical path (Wayland protocol, rendering, networking) is already portable
|
||||
- Forces clean separation of platform-specific backends behind traits
|
||||
- illumos Zones provide excellent session isolation (better than Linux namespaces)
|
||||
|
||||
## Consequences
|
||||
- Must compile Smithay with `default-features = false` and select features carefully
|
||||
- Need `cfg(target_os)` gates for platform-specific code paths
|
||||
- `memfd_create` fallback adds minor complexity to shared memory handling
|
||||
- PulseAudio support required alongside PipeWire for audio
|
||||
- Cannot rely on Linux-specific crates without feature-gating them
|
||||
- CI must test on both Linux and illumos (illumos CI runner or cross-compilation)
|
||||
209
docs/ai/adr/009-pluggable-window-management.md
Normal file
209
docs/ai/adr/009-pluggable-window-management.md
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
# ADR-009: Pluggable Window Management Protocol
|
||||
|
||||
## Status
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
One of SunRay's strengths in the Unix community was running on Solaris with its rich X11 ecosystem -- users could choose any window manager (CDE, FVWM, dwm, i3, etc.) because X11 cleanly separated the display server from window management via ICCCM/EWMH.
|
||||
|
||||
Wayland merged the compositor and window manager into one process, killing this flexibility. River (a Zig/wlroots compositor) pioneered re-separating them via a custom Wayland protocol (`river-window-management-v1`). WayRay should adopt this pattern to let users choose their workflow: floating, tiling, dynamic, or anything custom.
|
||||
|
||||
## Design
|
||||
|
||||
### Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────┐
|
||||
│ WayRay Compositor │
|
||||
│ │
|
||||
│ Wayland protocol impl │
|
||||
│ Rendering + encoding │
|
||||
│ Input dispatch │
|
||||
│ Session management │
|
||||
│ │
|
||||
│ ┌────────────────────┐ │
|
||||
│ │ WM Protocol Server │ │ <── Custom Wayland protocol
|
||||
│ └────────┬───────────┘ │
|
||||
└───────────┼──────────────┘
|
||||
│ Unix socket (Wayland protocol)
|
||||
│
|
||||
┌───────────┴──────────────┐
|
||||
│ Window Manager Process │
|
||||
│ │
|
||||
│ Layout algorithm │
|
||||
│ Focus policy │
|
||||
│ Keybinding handling │
|
||||
│ Decoration decisions │
|
||||
│ Workspace management │
|
||||
│ │
|
||||
│ (any language with │
|
||||
│ Wayland client bindings) │
|
||||
└───────────────────────────┘
|
||||
```
|
||||
|
||||
The WM is a regular Wayland client that connects to the compositor and binds the `wayray_window_manager_v1` global. Only one WM can be bound at a time.
|
||||
|
||||
### Two-Phase Transaction Model (from River)
|
||||
|
||||
Inspired by River's `river-window-management-v1`, all WM operations happen in two atomic phases:
|
||||
|
||||
**Phase 1: Manage (Policy Decisions)**
|
||||
1. Compositor sends `manage_start` when state changes occur (new window, close, resize request, fullscreen request)
|
||||
2. WM evaluates and responds with policy: `propose_dimensions`, `set_focus`, `use_ssd`/`use_csd`, `grant_fullscreen`/`deny_fullscreen`
|
||||
3. WM calls `manage_done`
|
||||
4. Compositor sends `xdg_toplevel.configure` to affected windows
|
||||
|
||||
**Phase 2: Render (Visual Placement)**
|
||||
1. After clients acknowledge configures and commit, compositor sends `render_start`
|
||||
2. WM specifies visual state: `set_position`, `set_z_order`, `set_borders`, `show`/`hide`
|
||||
3. WM calls `render_done`
|
||||
4. Compositor applies all changes atomically in one frame
|
||||
|
||||
### Protocol Interfaces
|
||||
|
||||
#### `wayray_wm_manager_v1` (Global)
|
||||
```xml
|
||||
<interface name="wayray_wm_manager_v1" version="1">
|
||||
<!-- Lifecycle -->
|
||||
<event name="window_new"> <!-- New toplevel appeared -->
|
||||
<event name="window_closed"> <!-- Toplevel destroyed -->
|
||||
|
||||
<!-- Manage phase -->
|
||||
<event name="manage_start">
|
||||
<request name="manage_done">
|
||||
|
||||
<!-- Render phase -->
|
||||
<event name="render_start">
|
||||
<request name="render_done">
|
||||
|
||||
<!-- Outputs -->
|
||||
<event name="output_new">
|
||||
<event name="output_removed">
|
||||
<event name="output_geometry"> <!-- size, scale, usable area -->
|
||||
</interface>
|
||||
```
|
||||
|
||||
#### `wayray_wm_window_v1` (Per-Window)
|
||||
```xml
|
||||
<interface name="wayray_wm_window_v1" version="1">
|
||||
<!-- Properties (events from compositor) -->
|
||||
<event name="title">
|
||||
<event name="app_id">
|
||||
<event name="parent"> <!-- Dialog parent -->
|
||||
<event name="size_hints"> <!-- min/max size, aspect ratio -->
|
||||
<event name="fullscreen_request">
|
||||
<event name="maximize_request">
|
||||
<event name="close_request"> <!-- User/app requested close -->
|
||||
<event name="dimensions"> <!-- Actual committed size after configure -->
|
||||
|
||||
<!-- Policy requests (WM -> compositor, manage phase) -->
|
||||
<request name="propose_dimensions"> <!-- width, height -->
|
||||
<request name="set_focus"> <!-- keyboard focus to this window -->
|
||||
<request name="use_ssd"> <!-- server-side decorations -->
|
||||
<request name="use_csd"> <!-- client-side decorations -->
|
||||
<request name="grant_fullscreen">
|
||||
<request name="deny_fullscreen">
|
||||
<request name="close"> <!-- tell client to close -->
|
||||
|
||||
<!-- Visual placement (WM -> compositor, render phase) -->
|
||||
<request name="set_position"> <!-- x, y relative to output -->
|
||||
<request name="set_z_above"> <!-- place above another window -->
|
||||
<request name="set_z_below"> <!-- place below another window -->
|
||||
<request name="set_z_top"> <!-- top of stack -->
|
||||
<request name="set_z_bottom"> <!-- bottom of stack -->
|
||||
<request name="set_borders"> <!-- color, width per edge -->
|
||||
<request name="show">
|
||||
<request name="hide">
|
||||
<request name="set_output"> <!-- assign to output -->
|
||||
</interface>
|
||||
```
|
||||
|
||||
#### `wayray_wm_seat_v1` (Input/Keybindings)
|
||||
```xml
|
||||
<interface name="wayray_wm_seat_v1" version="1">
|
||||
<!-- Keybinding registration -->
|
||||
<request name="bind_key"> <!-- key + modifiers + mode -->
|
||||
<request name="unbind_key">
|
||||
<request name="create_mode"> <!-- like i3 binding modes -->
|
||||
<request name="activate_mode">
|
||||
|
||||
<!-- Keybinding delivery -->
|
||||
<event name="binding_pressed">
|
||||
<event name="binding_released">
|
||||
|
||||
<!-- Pointer interactive operations -->
|
||||
<request name="start_move"> <!-- interactive window move -->
|
||||
<request name="start_resize"> <!-- interactive window resize -->
|
||||
</interface>
|
||||
```
|
||||
|
||||
#### `wayray_wm_workspace_v1` (Virtual Desktops / Tags)
|
||||
```xml
|
||||
<interface name="wayray_wm_workspace_v1" version="1">
|
||||
<request name="create_workspace">
|
||||
<request name="destroy_workspace">
|
||||
<request name="set_active_workspace"> <!-- per output -->
|
||||
<request name="assign_window"> <!-- window to workspace -->
|
||||
<request name="set_window_tags"> <!-- bitmask: window on multiple tags -->
|
||||
<event name="workspace_created">
|
||||
<event name="workspace_destroyed">
|
||||
</interface>
|
||||
```
|
||||
|
||||
### Supported WM Paradigms
|
||||
|
||||
| Paradigm | Example | How It Maps |
|
||||
|----------|---------|-------------|
|
||||
| Floating | Openbox, FVWM | Arbitrary `set_position` + `set_z_*` + interactive move/resize |
|
||||
| Tiling | i3, dwm | Calculate layout on `manage_start`, batch `propose_dimensions` + `set_position` |
|
||||
| Dynamic | awesome, xmonad | Switch layout algorithm, re-layout all windows atomically |
|
||||
| Stacking/keyboard | ratpoison, StumpWM | Fullscreen windows, `hide`/`show` + binding modes for prefix keys |
|
||||
| Scrolling | niri, PaperWM | Positions outside visible area, `set_position` with smooth offsets |
|
||||
| Tags | dwm | `set_window_tags` bitmask, windows visible on multiple tags simultaneously |
|
||||
|
||||
### Default WM
|
||||
|
||||
WayRay ships with a built-in **floating WM** as the default. If no external WM connects, the built-in WM handles window placement with sane defaults (centered new windows, basic keyboard shortcuts). When an external WM connects, the built-in WM yields.
|
||||
|
||||
### Hot-Swap and Crash Resilience
|
||||
|
||||
- If the WM process crashes, the compositor continues running. Windows freeze in their last positions. A new WM can connect and take over.
|
||||
- Hot-swap: a new WM can connect while the old one is running. The compositor sends a `replaced` event to the old WM, which should disconnect gracefully.
|
||||
- On WM connect, the compositor sends the full window list so the WM can reconstruct state.
|
||||
|
||||
## Options Considered
|
||||
|
||||
### 1. Monolithic (WM logic in compositor)
|
||||
- Simplest implementation
|
||||
- Cannot swap WMs without restarting compositor (and all sessions!)
|
||||
- Violates Unix philosophy
|
||||
- Rejected: kills the "choose your workflow" feature
|
||||
|
||||
### 2. In-process plugin (like Wayfire/Compiz)
|
||||
- WM loaded as a shared library
|
||||
- Fast (no IPC overhead)
|
||||
- Crash in WM crashes the compositor
|
||||
- Language-restricted (must be Rust or C FFI)
|
||||
- Rejected: not crash-resilient, not language-agnostic
|
||||
|
||||
### 3. External process via custom Wayland protocol (River model)
|
||||
- Clean separation, crash-resilient, hot-swappable
|
||||
- Language-agnostic (any Wayland client library)
|
||||
- Small IPC overhead (negligible: manage/render happen once per frame at most)
|
||||
- More complex to implement
|
||||
- **Selected**: best fit for WayRay's values
|
||||
|
||||
## Rationale
|
||||
- Honors the X11 tradition of WM choice that Unix enthusiasts loved
|
||||
- A SunRay replacement that only offers one workflow would alienate the community
|
||||
- River proved this works in practice with their two-phase model
|
||||
- Crash resilience is critical for a thin client server (WM crash must not kill user sessions)
|
||||
- Enables a community ecosystem of WMs in any language
|
||||
- The two-phase transaction model prevents visual glitches (no partial layouts visible)
|
||||
|
||||
## Consequences
|
||||
- Must implement and maintain a custom Wayland protocol
|
||||
- Default floating WM adds code but is necessary for out-of-box usability
|
||||
- WM developers need documentation and example implementations
|
||||
- Slightly more latency than monolithic (one extra IPC roundtrip per layout change, ~microseconds)
|
||||
- The protocol must be versioned carefully; breaking changes affect all WM implementations
|
||||
200
docs/ai/plans/001-implementation-roadmap.md
Normal file
200
docs/ai/plans/001-implementation-roadmap.md
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
# Implementation Roadmap
|
||||
|
||||
## Phase 0: Foundation (Weeks 1-2)
|
||||
|
||||
### 0.1 Project Structure
|
||||
- Set up Cargo workspace with four crates: `wayray-server`, `wayray-client`, `wayray-protocol`, `wayray-ctl`
|
||||
- Configure shared dependencies, feature flags, CI (Linux + illumos)
|
||||
- Set up tracing/logging infrastructure with miette error handling
|
||||
- Smithay with `default-features = false` + portable features only in core
|
||||
- Platform-specific backends behind `cfg(target_os)` + feature flags
|
||||
|
||||
### 0.2 Minimal Compositor (Server)
|
||||
- Implement a minimal Smithay compositor based on Smallvil patterns
|
||||
- Support: wl_compositor, xdg_shell, wl_shm, wl_seat, wl_output
|
||||
- Use Winit backend for development/testing (works on both Linux and illumos)
|
||||
- Use `shm_open` with `memfd_create` fallback for shared memory portability
|
||||
- Verify basic Wayland clients (foot terminal, weston-info) can connect and render
|
||||
|
||||
### 0.3 Framebuffer Capture
|
||||
- Implement ExportMem-based framebuffer capture after each render pass
|
||||
- Integrate OutputDamageTracker for efficient dirty region tracking
|
||||
- Benchmark: capture latency, memory bandwidth
|
||||
|
||||
## Phase 1: Network Protocol (Weeks 3-5)
|
||||
|
||||
### 1.1 Protocol Definition
|
||||
- Define WayRay wire protocol in wayray-protocol crate
|
||||
- Message types: FrameUpdate, InputEvent, SessionControl, AudioChunk, USBData
|
||||
- Serialization: serde + bincode or postcard for low overhead
|
||||
- Version negotiation and capability exchange
|
||||
|
||||
### 1.2 QUIC Transport Layer
|
||||
- Implement QUIC server (quinn) in wayray-server
|
||||
- Implement QUIC client (quinn) in wayray-client
|
||||
- Stream mapping:
|
||||
- Stream 0: Control channel (session mgmt, capabilities)
|
||||
- Stream 1: Display channel (frame updates, damage regions)
|
||||
- Stream 2: Input channel (keyboard, mouse, touch)
|
||||
- Stream 3: Audio channel (Opus frames)
|
||||
- Stream 4+: USB device channels (one per device)
|
||||
- Connection handling: TLS certificates, authentication
|
||||
|
||||
### 1.3 Frame Encoding Pipeline
|
||||
- Implement frame differencing (XOR diff against previous frame)
|
||||
- Implement region-based compression (zstd for lossless regions)
|
||||
- Implement content-adaptive encoding:
|
||||
- Static regions: lossless zstd diff
|
||||
- Video regions: H.264 via ffmpeg-next or VAAPI
|
||||
- Text regions: lossless PNG-style encoding
|
||||
- Damage rectangle merging and optimization
|
||||
|
||||
## Phase 2: Client Viewer (Weeks 5-7)
|
||||
|
||||
### 2.1 Display Client
|
||||
- Implement wayray-client as a standalone application
|
||||
- Use winit + wgpu for cross-platform display
|
||||
- Frame decoding pipeline: receive -> decompress -> decode -> upload to GPU -> display
|
||||
- Double-buffered rendering with VSync
|
||||
|
||||
### 2.2 Input Capture & Forwarding
|
||||
- Capture keyboard events (with proper keymap forwarding via xkb)
|
||||
- Capture mouse events (motion, buttons, scroll)
|
||||
- Capture touch events
|
||||
- Serialize and send over QUIC input stream
|
||||
- Handle keyboard grab/release for compositor key passthrough
|
||||
|
||||
### 2.3 Cursor Handling
|
||||
- Server-side cursor rendering (simplest)
|
||||
- Client-side cursor rendering with cursor image forwarding (lower latency)
|
||||
- Cursor shape protocol support
|
||||
|
||||
## Phase 2.5: Pluggable Window Management (Weeks 7-8)
|
||||
|
||||
### 2.5.1 WM Protocol Definition
|
||||
- Define `wayray_wm_manager_v1` Wayland protocol XML
|
||||
- Define `wayray_wm_window_v1`, `wayray_wm_seat_v1`, `wayray_wm_workspace_v1`
|
||||
- Implement two-phase transaction model (manage + render sequences)
|
||||
- Generate Rust bindings via wayland-scanner
|
||||
|
||||
### 2.5.2 WM Protocol Server (in compositor)
|
||||
- Implement WM global in wayray-server
|
||||
- Window lifecycle events (new, closed, properties)
|
||||
- Manage phase: receive policy decisions, send configures
|
||||
- Render phase: apply positions/z-order atomically
|
||||
- Keybinding registration and dispatch via seat interface
|
||||
|
||||
### 2.5.3 Built-in Floating WM
|
||||
- Default WM active when no external WM is connected
|
||||
- Basic floating behavior: centered new windows, focus-follows-click
|
||||
- Keyboard shortcuts: Alt+F4 close, Alt+Tab cycle, Super+Arrow snap
|
||||
- Yields to external WM on connect
|
||||
|
||||
### 2.5.4 Example Tiling WM
|
||||
- Ship a reference tiling WM as a separate binary (`wayray-wm-tiling`)
|
||||
- Demonstrates the protocol for third-party WM developers
|
||||
- Basic BSP tiling with keyboard-driven focus
|
||||
|
||||
## Phase 3: Session Management (Weeks 8-11)
|
||||
|
||||
### 3.1 Session Persistence
|
||||
- Session state machine: Created -> Active -> Suspended -> Resumed -> Destroyed
|
||||
- Session storage: in-memory with optional persistence (SeaORM + SQLite)
|
||||
- Session timeout and cleanup policies
|
||||
|
||||
### 3.2 Authentication
|
||||
- Token-based session identification (smart card ID, badge, or software token)
|
||||
- PAM integration for user authentication
|
||||
- Session-token binding in session store
|
||||
|
||||
### 3.3 Hot-Desking (Session Mobility)
|
||||
- Token insertion triggers session lookup across server pool
|
||||
- Session reconnection: rebind existing session to new client endpoint
|
||||
- Session disconnect: unbind from client, keep session running
|
||||
- Sub-second reconnection target (< 500ms)
|
||||
|
||||
### 3.4 Multi-Server Support
|
||||
- Server discovery protocol (mDNS or custom)
|
||||
- Session registry: which sessions live on which servers
|
||||
- Cross-server session redirect
|
||||
- Load balancing for new session placement
|
||||
|
||||
## Phase 4: Audio & Peripherals (Weeks 10-13)
|
||||
|
||||
### 4.1 Audio Forwarding
|
||||
- Trait-based audio backend: PipeWire (Linux), PulseAudio (illumos/Linux fallback)
|
||||
- PipeWire integration on server side for audio capture (Linux)
|
||||
- Opus encoding for low-latency audio streaming
|
||||
- Audio stream over dedicated QUIC stream
|
||||
- Playback synchronization with display frames
|
||||
- Microphone input forwarding (bidirectional audio)
|
||||
|
||||
### 4.2 USB Device Forwarding
|
||||
- Userspace USB forwarding protocol over QUIC (not kernel USB/IP, for illumos portability)
|
||||
- Consider usbredir as wire format or design custom
|
||||
- Device hotplug detection on client (udev on Linux, sysevent on illumos)
|
||||
- Device attach/detach over QUIC channels
|
||||
- Security: device class filtering (allow/deny policies)
|
||||
|
||||
### 4.3 Clipboard Synchronization
|
||||
- Intercept wl_data_device on server
|
||||
- Forward clipboard content types and data over control channel
|
||||
- Handle large clipboard entries (images) efficiently
|
||||
- Security: optional clipboard direction restrictions
|
||||
|
||||
## Phase 5: Production Hardening (Weeks 14-17)
|
||||
|
||||
### 5.1 Platform-Specific Backends
|
||||
- Linux: DRM/KMS backend for running wayray-server on hardware (optional, feature-gated)
|
||||
- Linux: Multi-GPU support via MultiRenderer
|
||||
- Linux: Session management via logind/libseat
|
||||
- illumos: Custom input backend for `/dev/kbd` + `/dev/mouse` (local console use)
|
||||
- illumos: Zones integration for session isolation
|
||||
- CI: Test matrix for both Linux and illumos
|
||||
|
||||
### 5.2 XWayland Support
|
||||
- Integrate Smithay's XWayland module for X11 application compatibility
|
||||
- Handle X11 clipboard integration
|
||||
|
||||
### 5.3 Performance Optimization
|
||||
- Adaptive bitrate based on network conditions
|
||||
- Hardware encoding path (VAAPI, NVENC)
|
||||
- Zero-copy frame capture via DMA-BUF export to encoder
|
||||
- Client-side frame interpolation for network jitter compensation
|
||||
|
||||
### 5.4 Security
|
||||
- TLS 1.3 for all QUIC connections (mandatory)
|
||||
- Certificate-based mutual authentication
|
||||
- Session encryption at rest
|
||||
- Audit logging for session lifecycle events
|
||||
- AppArmor/seccomp profiles for server process
|
||||
|
||||
## Phase 6: Management & Operations (Weeks 16-20)
|
||||
|
||||
### 6.1 Administration
|
||||
- CLI tool: `wayray-ctl` for server/session management
|
||||
- REST API for external integration
|
||||
- Session monitoring: active sessions, resource usage, network stats
|
||||
|
||||
### 6.2 Multi-Tenancy
|
||||
- User session isolation (namespaces, cgroups)
|
||||
- Resource quotas (CPU, memory, GPU per session)
|
||||
- Fair scheduling across sessions
|
||||
|
||||
### 6.3 High Availability
|
||||
- Server failover groups
|
||||
- Session state replication for seamless failover
|
||||
- Health checking and automatic server removal
|
||||
|
||||
## Milestones
|
||||
|
||||
| Milestone | Phase | Description |
|
||||
|-----------|-------|-------------|
|
||||
| M0 | 0 | Wayland clients render in local compositor |
|
||||
| M1 | 1 | Remote viewer sees compositor output over network |
|
||||
| M2 | 2 | Interactive remote session (display + input) |
|
||||
| M2.5 | 2.5 | External WM can control window layout via protocol |
|
||||
| M3 | 3 | Session persists across client disconnects, hot-desking works |
|
||||
| M4 | 4 | Audio and USB forwarding functional |
|
||||
| M5 | 5 | Production-ready with platform backends, XWayland, illumos CI |
|
||||
| M6 | 6 | Multi-server deployment with HA and management tools |
|
||||
160
docs/architecture/overview.md
Normal file
160
docs/architecture/overview.md
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
# WayRay Architecture Overview
|
||||
|
||||
## Vision
|
||||
|
||||
WayRay is a modern reimagining of Oracle/Sun's SunRay thin client system, built as a native Wayland compositor. Where SunRay used proprietary hardware and the ALP protocol over UDP, WayRay uses commodity hardware, the Wayland protocol for local application rendering, and QUIC for network transport.
|
||||
|
||||
## System Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ WayRay Server │
|
||||
│ │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ App 1 │ │ App 2 │ │ App N │ Wayland │
|
||||
│ │ (foot) │ │ (firefox)│ │ (...) │ Clients │
|
||||
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
|
||||
│ │ │ │ │
|
||||
│ └──────────────┼──────────────┘ │
|
||||
│ │ Wayland Protocol │
|
||||
│ v │
|
||||
│ ┌───────────────────────────────────────┐ │
|
||||
│ │ Smithay Compositor │ │
|
||||
│ │ │ │
|
||||
│ │ ┌─────────┐ ┌──────────┐ │ │
|
||||
│ │ │ XDG │ │ Layer │ Protocol │ │
|
||||
│ │ │ Shell │ │ Shell │ Handlers │ │
|
||||
│ │ └─────────┘ └──────────┘ │ │
|
||||
│ │ │ │
|
||||
│ │ ┌─────────┐ ┌──────────┐ │ │
|
||||
│ │ │ Seat │ │ Output │ │ │
|
||||
│ │ │ (input) │ │ Manager │ │ │
|
||||
│ │ └─────────┘ └──────────┘ │ │
|
||||
│ │ │ │
|
||||
│ │ ┌─────────────────────────┐ │ │
|
||||
│ │ │ Renderer │ │ │
|
||||
│ │ │ (Pixman or GLES) │ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ │ OutputDamageTracker │ │ │
|
||||
│ │ │ ExportMem (framebuffer) │ │ │
|
||||
│ │ └─────────────────────────┘ │ │
|
||||
│ └───────────────┬───────────────────────┘ │
|
||||
│ │ │
|
||||
│ v │
|
||||
│ ┌───────────────────────────────────────┐ │
|
||||
│ │ Frame Encoder │ │
|
||||
│ │ │ │
|
||||
│ │ Damage Regions -> Tile Classification│ │
|
||||
│ │ -> Lossless (zstd) / Lossy (H.264) │ │
|
||||
│ └───────────────┬───────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌───────────────┴───────────────────────┐ │
|
||||
│ │ Session Manager │ │
|
||||
│ │ │ │
|
||||
│ │ Token -> Session mapping │ │
|
||||
│ │ Session lifecycle │ │
|
||||
│ │ Multi-server coordination │ │
|
||||
│ └───────────────┬───────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌───────────────┴───────────────────────┐ │
|
||||
│ │ QUIC Transport (quinn) │ │
|
||||
│ │ │ │
|
||||
│ │ Stream 0: Control │ │
|
||||
│ │ Stream 1: Display (frames) │ │
|
||||
│ │ Stream 2: Input (keyboard/mouse) │ │
|
||||
│ │ Stream 3: Audio (Opus) │ │
|
||||
│ │ Stream 4+: USB devices │ │
|
||||
│ └───────────────┬───────────────────────┘ │
|
||||
└──────────────────┼───────────────────────────────────────┘
|
||||
│
|
||||
│ QUIC / TLS 1.3
|
||||
│ (Network)
|
||||
│
|
||||
┌──────────────────┼───────────────────────────────────────┐
|
||||
│ │ WayRay Client │
|
||||
│ ┌───────────────┴───────────────────────┐ │
|
||||
│ │ QUIC Transport (quinn) │ │
|
||||
│ └───────────────┬───────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌───────────┬───┴──────┬────────────┬───────────┐ │
|
||||
│ │ │ │ │ │ │
|
||||
│ v v v v v │
|
||||
│ ┌─────┐ ┌──────┐ ┌────────┐ ┌────────┐ ┌─────┐ │
|
||||
│ │Ctrl │ │Frame │ │ Input │ │ Audio │ │ USB │ │
|
||||
│ │ │ │Decode│ │Capture │ │Play/Cap│ │Fwd │ │
|
||||
│ └─────┘ └──┬───┘ └───┬────┘ └────────┘ └─────┘ │
|
||||
│ │ │ │
|
||||
│ v │ │
|
||||
│ ┌──────────────┐ │ │
|
||||
│ │ wgpu/winit │ │ │
|
||||
│ │ Display │<─────┘ (cursor, HiDPI) │
|
||||
│ └──────────────┘ │
|
||||
│ │
|
||||
│ ┌──────────────┐ │
|
||||
│ │ Token │ Smart Card / NFC / Software Token │
|
||||
│ │ Provider │ │
|
||||
│ └──────────────┘ │
|
||||
└───────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Core Principles
|
||||
|
||||
### 1. Stateless Client
|
||||
The WayRay client stores no user data, no session state, and no application state. If the client device is lost or destroyed, nothing is lost. All state lives on the server.
|
||||
|
||||
### 2. Session Mobility
|
||||
A session is bound to a token, not a device. Insert your token into any WayRay client on the network and your desktop follows you -- all windows, all applications, exact state preserved.
|
||||
|
||||
### 3. Server-Side Rendering
|
||||
All Wayland clients run on the server. The compositor renders everything server-side and transmits compressed frames to the client. The client is purely a decoder + display + input capture device.
|
||||
|
||||
### 4. Adaptive Encoding
|
||||
Frame encoding adapts to content type and network conditions:
|
||||
- Text/UI: lossless compression (perfect rendering)
|
||||
- Video: lossy hardware encoding (H.264/AV1)
|
||||
- Low bandwidth: aggressive compression, lower framerate
|
||||
- LAN: minimal compression, high framerate
|
||||
|
||||
### 5. Security by Default
|
||||
- All network traffic encrypted via TLS 1.3 (QUIC mandates it)
|
||||
- No data at rest on client devices
|
||||
- Session authentication via PAM
|
||||
- USB device policies for access control
|
||||
|
||||
## Data Flow
|
||||
|
||||
### Display (Server -> Client)
|
||||
1. Wayland applications commit surface updates
|
||||
2. Smithay compositor renders all surfaces into output framebuffer
|
||||
3. OutputDamageTracker identifies changed regions since last frame
|
||||
4. Frame encoder processes damaged regions (tile-based, content-adaptive)
|
||||
5. Encoded frame sent over QUIC display stream
|
||||
6. Client decodes frame regions, uploads to GPU, displays
|
||||
|
||||
### Input (Client -> Server)
|
||||
1. Client captures keyboard/mouse/touch events
|
||||
2. Events serialized with timestamps and keymaps
|
||||
3. Sent over QUIC input stream (highest priority)
|
||||
4. Server deserializes and injects into Smithay Seat
|
||||
5. Seat forwards to focused Wayland client
|
||||
|
||||
### Audio (Bidirectional)
|
||||
1. Server applications output audio to PipeWire virtual sink
|
||||
2. Audio frames encoded to Opus, sent over QUIC audio stream
|
||||
3. Client decodes Opus, plays through local audio device
|
||||
4. Client captures microphone, encodes to Opus, sends to server
|
||||
5. Server feeds decoded audio into PipeWire virtual source
|
||||
|
||||
## Comparison with SunRay
|
||||
|
||||
| Feature | SunRay | WayRay |
|
||||
|---------|--------|--------|
|
||||
| Display Protocol | ALP (proprietary, UDP) | WayRay Protocol (QUIC) |
|
||||
| Compositor | X server (Xnewt) | Wayland (Smithay) |
|
||||
| Client Hardware | Purpose-built DTU | Any Linux/macOS/Windows device |
|
||||
| Session Mobility | Smart card | Pluggable tokens (smart card, NFC, software) |
|
||||
| Audio | Custom ALP channel | Opus over QUIC |
|
||||
| USB Forwarding | Custom | USB/IP or usbredir |
|
||||
| Encryption | Optional ALP encryption | Mandatory TLS 1.3 |
|
||||
| Multi-server | Failover groups | Distributed session registry |
|
||||
| Client OS | Proprietary RTOS | Native application |
|
||||
270
docs/protocols/wayray-wire-protocol.md
Normal file
270
docs/protocols/wayray-wire-protocol.md
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
# WayRay Wire Protocol Specification
|
||||
|
||||
## Overview
|
||||
|
||||
The WayRay wire protocol defines communication between WayRay server and client over QUIC. All messages are serialized with postcard (compact binary, no-std compatible) and transmitted over typed QUIC streams.
|
||||
|
||||
## Protocol Version
|
||||
|
||||
Protocol version is exchanged during the initial handshake on the control stream. Both sides must agree on a compatible version before proceeding.
|
||||
|
||||
```
|
||||
ProtocolVersion {
|
||||
major: u16, // Breaking changes
|
||||
minor: u16, // Additive features
|
||||
patch: u16, // Bug fixes
|
||||
}
|
||||
```
|
||||
|
||||
## QUIC Stream Layout
|
||||
|
||||
### Stream 0: Control (Bidirectional, Reliable)
|
||||
Session management, capability negotiation, configuration.
|
||||
|
||||
### Stream 1: Display (Server -> Client, Semi-Reliable)
|
||||
Frame updates. Old undelivered frames may be skipped in favor of newer ones.
|
||||
|
||||
### Stream 2: Input (Client -> Server, Reliable)
|
||||
Keyboard, mouse, touch, tablet input events.
|
||||
|
||||
### Stream 3: Audio (Bidirectional, Semi-Reliable)
|
||||
Opus-encoded audio frames. Tolerant of packet loss.
|
||||
|
||||
### Stream 4+: USB (Bidirectional, Reliable)
|
||||
One stream per USB device. USB/IP or usbredir encapsulated data.
|
||||
|
||||
## Message Format
|
||||
|
||||
Every message on the wire is framed as:
|
||||
|
||||
```
|
||||
┌──────────┬──────────┬──────────────────┐
|
||||
│ type: u8 │ len: u32 │ payload: [u8] │
|
||||
└──────────┴──────────┴──────────────────┘
|
||||
```
|
||||
|
||||
- `type`: Message type discriminant
|
||||
- `len`: Payload length in bytes (little-endian)
|
||||
- `payload`: Postcard-serialized message body
|
||||
|
||||
## Control Messages (Stream 0)
|
||||
|
||||
### ClientHello
|
||||
Client -> Server. Sent immediately after QUIC connection established.
|
||||
|
||||
```rust
|
||||
ClientHello {
|
||||
protocol_version: ProtocolVersion,
|
||||
token: SessionToken, // Session identity
|
||||
client_capabilities: ClientCaps,
|
||||
display_info: Vec<DisplayInfo>, // Client's physical displays
|
||||
}
|
||||
|
||||
ClientCaps {
|
||||
max_width: u32,
|
||||
max_height: u32,
|
||||
supports_h264: bool,
|
||||
supports_av1: bool,
|
||||
supports_opus: bool,
|
||||
supports_usb: bool,
|
||||
preferred_refresh_rate: u32,
|
||||
}
|
||||
|
||||
DisplayInfo {
|
||||
width: u32,
|
||||
height: u32,
|
||||
scale_factor: f64,
|
||||
refresh_rate: u32,
|
||||
}
|
||||
```
|
||||
|
||||
### ServerHello
|
||||
Server -> Client. Response to ClientHello.
|
||||
|
||||
```rust
|
||||
ServerHello {
|
||||
protocol_version: ProtocolVersion,
|
||||
session_id: Uuid,
|
||||
session_state: SessionState, // New, Resumed
|
||||
server_capabilities: ServerCaps,
|
||||
output_config: OutputConfig, // Assigned resolution/refresh
|
||||
keymap: Vec<u8>, // XKB keymap for input
|
||||
}
|
||||
```
|
||||
|
||||
### SessionSuspend / SessionResume
|
||||
Bidirectional. Lifecycle transitions.
|
||||
|
||||
### Ping / Pong
|
||||
Bidirectional. Latency measurement and keepalive.
|
||||
|
||||
## Display Messages (Stream 1)
|
||||
|
||||
### FrameUpdate
|
||||
Server -> Client. Contains one or more encoded regions.
|
||||
|
||||
```rust
|
||||
FrameUpdate {
|
||||
sequence: u64,
|
||||
timestamp_us: u64,
|
||||
cursor: Option<CursorUpdate>,
|
||||
regions: Vec<EncodedRegion>,
|
||||
}
|
||||
|
||||
EncodedRegion {
|
||||
x: u32,
|
||||
y: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
encoding: RegionEncoding,
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
enum RegionEncoding {
|
||||
Raw, // Uncompressed RGBA
|
||||
Zstd, // Zstd-compressed diff against previous frame
|
||||
Jpeg, // JPEG for photographic content
|
||||
H264, // H.264 NAL units
|
||||
Av1, // AV1 OBU
|
||||
}
|
||||
|
||||
CursorUpdate {
|
||||
x: i32,
|
||||
y: i32,
|
||||
shape: Option<CursorShape>, // Only sent when shape changes
|
||||
}
|
||||
|
||||
CursorShape {
|
||||
width: u32,
|
||||
height: u32,
|
||||
hotspot_x: u32,
|
||||
hotspot_y: u32,
|
||||
pixels: Vec<u8>, // RGBA
|
||||
}
|
||||
```
|
||||
|
||||
### FrameAck
|
||||
Client -> Server. Acknowledges frame receipt for flow control.
|
||||
|
||||
```rust
|
||||
FrameAck {
|
||||
sequence: u64,
|
||||
render_time_us: u64, // How long client took to decode + render
|
||||
}
|
||||
```
|
||||
|
||||
## Input Messages (Stream 2)
|
||||
|
||||
### KeyboardEvent
|
||||
|
||||
```rust
|
||||
KeyboardEvent {
|
||||
timestamp_us: u64,
|
||||
keycode: u32,
|
||||
state: KeyState, // Pressed, Released
|
||||
modifiers: Modifiers, // Current modifier state
|
||||
}
|
||||
```
|
||||
|
||||
### PointerMotion
|
||||
|
||||
```rust
|
||||
PointerMotion {
|
||||
timestamp_us: u64,
|
||||
x: f64, // Absolute position in output coordinates
|
||||
y: f64,
|
||||
}
|
||||
```
|
||||
|
||||
### PointerButton
|
||||
|
||||
```rust
|
||||
PointerButton {
|
||||
timestamp_us: u64,
|
||||
button: u32,
|
||||
state: ButtonState,
|
||||
}
|
||||
```
|
||||
|
||||
### PointerAxis
|
||||
|
||||
```rust
|
||||
PointerAxis {
|
||||
timestamp_us: u64,
|
||||
axis: Axis, // Horizontal, Vertical
|
||||
value: f64,
|
||||
discrete: Option<i32>,
|
||||
}
|
||||
```
|
||||
|
||||
### TouchEvent
|
||||
|
||||
```rust
|
||||
TouchEvent {
|
||||
timestamp_us: u64,
|
||||
touch_type: TouchType, // Down, Up, Motion, Frame, Cancel
|
||||
id: i32,
|
||||
x: f64,
|
||||
y: f64,
|
||||
}
|
||||
```
|
||||
|
||||
## Audio Messages (Stream 3)
|
||||
|
||||
### AudioFrame
|
||||
|
||||
```rust
|
||||
AudioFrame {
|
||||
timestamp_us: u64,
|
||||
channels: u8,
|
||||
sample_rate: u32,
|
||||
codec: AudioCodec, // Opus
|
||||
data: Vec<u8>, // Encoded audio data
|
||||
}
|
||||
```
|
||||
|
||||
### AudioConfig
|
||||
Sent during session setup to negotiate audio parameters.
|
||||
|
||||
```rust
|
||||
AudioConfig {
|
||||
sample_rate: u32, // 48000
|
||||
channels: u8, // 2 (stereo)
|
||||
frame_duration_ms: u8, // 20
|
||||
codec: AudioCodec,
|
||||
}
|
||||
```
|
||||
|
||||
## USB Messages (Stream 4+)
|
||||
|
||||
### USBDeviceAttach
|
||||
|
||||
```rust
|
||||
USBDeviceAttach {
|
||||
device_id: u32,
|
||||
vendor_id: u16,
|
||||
product_id: u16,
|
||||
device_class: u8,
|
||||
device_subclass: u8,
|
||||
device_protocol: u8,
|
||||
description: String,
|
||||
}
|
||||
```
|
||||
|
||||
### USBDeviceDetach
|
||||
|
||||
```rust
|
||||
USBDeviceDetach {
|
||||
device_id: u32,
|
||||
}
|
||||
```
|
||||
|
||||
### USBData
|
||||
Encapsulated USB/IP or usbredir protocol data.
|
||||
|
||||
```rust
|
||||
USBData {
|
||||
device_id: u32,
|
||||
data: Vec<u8>,
|
||||
}
|
||||
```
|
||||
Loading…
Add table
Reference in a new issue