Skip to content

AusAgentSmith-org/remote-rs

Repository files navigation

remote-rs

A Rust remote desktop for Ubuntu / GNOME (Wayland). Screen capture via XDG portals, H.264 encoding, QUIC transport, input injection. Linux server, Linux + Windows client.

Status — v0.2.18

Feature State
GNOME portal screen capture (ScreenCast + RemoteDesktop)
H.264 encoding — software (openh264)
H.264 encoding — hardware VA-API (h264_vaapi, AMD/Intel) --encoder hardware
QUIC transport (quinn) + argon2 auth
Input injection — pointer + keyboard + scroll
Desktop audio (PipeWire → cpal)
Multi-client (first gets input control, rest view-only)
Restore token — skips portal dialog on subsequent starts
Client letterboxing + aspect-ratio-correct scaling
TLS cert pinning (--cert) ✅ optional
Windows client binary (cross-compiled in CI)
Client launcher GUI (Linux + Windows)
Server control GUI (Linux user service)
DMA-BUF zero-copy into VA-API ❌ AMD radeonsi doesn't support RGB32 DRM_PRIME_2 import
Clipboard / file transfer ⏳ not planned
GDM / login screen ❌ out of scope (portal requires logged-in user)

Layout

remote-rs/
├── Cargo.toml                # workspace
├── .woodpecker.yml           # CI: fmt/check/test/clippy + Linux + Windows build + Forgejo release on tag
└── crates/
    ├── remote-protocol/      # bincode wire types — handshake, control, input, frame/audio headers
    ├── remote-server/        # portal capture, PipeWire audio, VA-API encoding, QUIC server
    ├── remote-client/        # winit window, softbuffer renderer, openh264 decoder, cpal audio, QUIC client
    ├── remote-client-gui/    # native launcher for non-technical client setup
    └── remote-server-control/ # Linux GUI for installing/configuring the user service

Build

sudo apt-get install -y \
    libpipewire-0.3-dev libei-dev libeis-dev liboeffis-dev libclang-dev pkg-config \
    libasound2-dev \
    libavcodec-dev libavutil-dev libva-dev   # hardware-encoding only

# Software encoder (no GPU deps)
cargo build --release -p remote-server
cargo build --release -p remote-client
cargo build --release -p remote-client-gui
cargo build --release -p remote-server-control

# Hardware VA-API encoder
cargo build --release -p remote-server --features hardware-encoding

Run

# Server — software encoder
./target/release/remote-server --bind 0.0.0.0:7878 --password hunter2

# Server — hardware VA-API encoder (AMD/Intel GPU)
./target/release/remote-server --bind 0.0.0.0:7878 --password hunter2 --encoder hardware

# Server control GUI — Linux user service
./target/release/remote-server-control

# Client — Linux
./target/release/remote-client --server 192.168.1.50:7878 --password hunter2 --width 1920 --height 1080

# Client launcher GUI — Linux / Windows
./target/release/remote-client-gui

# Client — Windows (pre-built .exe from Forgejo release)
remote-client-vX.Y.Z-windows-x86_64.exe --server 192.168.1.50:7878 --password hunter2 --width 1920 --height 1080

# Client — auto size to the primary monitor
remote-client-vX.Y.Z-windows-x86_64.exe --server 192.168.1.50:7878 --password hunter2 --auto-resolution

The first server start pops the GNOME portal dialog — pick the monitor to share. The restore token is saved to ~/.config/remote-rs/restore-token; subsequent starts skip the dialog. Delete the file to force it to reappear.

Server control GUI

remote-server-control is a Linux GUI for the user service. It can copy a released remote-server binary from the same folder into ~/.local/bin/remote-server, write runtime settings to ~/.config/remote-rs/env, write the shared password to ~/.config/remote-rs/password with owner-only permissions, install remote-server.service into ~/.config/systemd/user/, and run systemctl --user actions for enable, disable, start, stop, and restart.

Package remote-server-control, remote-server, and remote-server.service together so the control GUI can install the server binary and service unit.

Environment variables

All CLI flags have env var equivalents: REMOTE_PASSWORD, REMOTE_SERVER, REMOTE_CERT, REMOTE_ENCODER, REMOTE_BIND, REMOTE_MONITOR, REMOTE_BITRATE_MBPS, REMOTE_MAX_FPS.

Server flags

Flag Default Notes
--bind 0.0.0.0:7878 UDP/QUIC listen address
--password required argon2-hashed at startup
--password-file alternative to --password; never appears in ps
--encoder auto auto | software | hardware
--monitor 0 which portal stream index to capture
--bitrate-mbps 4 H.264 target bitrate
--max-fps 60 0 = unlimited

Client flags

Flag Default Notes
--server required host:port
--password required
--auto-resolution false size the window from the primary monitor
--width / --height 1920 / 1080 initial window & desired logical resolution
--scale 1.0 HiDPI scale factor
--cert path to server's server.crt for pinning

Client launcher GUI

remote-client-gui is a small native launcher for non-technical users. It collects the server address, password, certificate path, and display sizing options, saves the non-secret settings under the user's config directory, then starts its embedded streaming client mode. The password is passed to the child process through REMOTE_PASSWORD, kept in memory only, and is not written to disk.

The standalone remote-client CLI is still released for scripted use, but the GUI launcher no longer needs to sit next to it.

Architecture

Client                              Server (Ubuntu / GNOME, Wayland)
──────                              ─────────────────────────────────
winit window                        ashpd ScreenCast+RemoteDesktop portal
  ├── softbuffer renderer             │  one combined session, keyboard+pointer
  ├── openh264 decoder                │  restore token skips dialog after first run
  ├── cpal audio output               │
  ├── input → control stream          ▼
  └── QUIC client (quinn)         PwVideoCapture (native pipewire crate)
       │ TLS self-signed               │  SHM frames → broadcast::Sender<CapturedFrame>
       │ ALPN: remote-rs/1             │
       │                               ▼
       │  control stream (bidi):   H264Encoder
       │   ClientHello/ServerHello   ├── Software: openh264 (BGRA→I420→H.264)
       │   ClientControl::{          └── Hardware: BGRA→NV12 (CPU) → av_hwframe_transfer_data
       │     Input, Resize, Bye}                   → h264_vaapi (GPU)
       │                               │
       │  video frames (uni):          │  PwAudioCapture (native pipewire crate)
       │   STREAM_VIDEO tag             │  → 48 kHz stereo F32 → broadcast
       │   FrameHeader + H.264 NALs    │
       │                               ▼
       │  audio stream (uni):      QUIC server (quinn)
       │   STREAM_AUDIO tag          ├── per-client pump_frames task
       │   AudioHeader + F32 PCM     └── per-client pump_audio task
       │
       └──── input injection: portal notify_pointer_* / notify_keyboard_keysym

Key design choices

  • One QUIC unidirectional stream per frame. The network drops stale frames cleanly under congestion without head-of-line blocking on the control stream.
  • Client-logical → capture-pixel mapping is server-side. The client sends events in its own window coordinates and reports its current window size via Resize. The server scales them to the captured monitor's pixel space before injecting.
  • Argon2 auth over TLS. Password travels over the QUIC TLS channel; server stores only the argon2 hash.
  • SHM frames always. AMD's radeonsi VAAPI driver rejects RGB32 DRM_PRIME_2 surface import (VA_STATUS_ERROR_INVALID_PARAMETER), so DMA-BUF zero-copy is disabled. SHM gives CPU-accessible BGRA frames; the hardware encoder does CPU BGRA→NV12 + av_hwframe_transfer_data before GPU H.264 encode (~11ms total at 3440×1440 on RX 7800 XT, CPU-bound on the colour conversion).
  • Native pipewire + ashpd. No third-party screen-capture wrappers — direct use of the pipewire and ashpd crates.

Troubleshooting

  • "portal session returned zero streams": dismissed the dialog or unchecked every monitor. Re-run.
  • Blank window / no frames: add RUST_LOG=remote_server=debug and check for errors in the PipeWire or encoder path.
  • InvalidCertificate on the client: pass --cert ~/.config/remote-rs/server.crt (copy from the server) or omit --cert for skip-verify.
  • Want to re-pick the monitor: rm ~/.config/remote-rs/restore-token and restart the server.
  • Hardware encoder not found: ensure libavcodec-dev libavutil-dev libva-dev are installed and rebuild with --features hardware-encoding. Falls back to software automatically in --encoder auto mode.

Releases

Pre-built binaries at repo.indexarr.net/indexarr/remote-rs/releases:

  • remote-server-vX.Y.Z-linux-x86_64 — server (software encoder, no GPU deps)
  • remote-server-vX.Y.Z-linux-x86_64-hw — server with VA-API hardware encoder
  • remote-server — unversioned software server binary used by remote-server-control installer
  • remote-server-control-vX.Y.Z-linux-x86_64 — Linux server service GUI
  • remote-server.service — systemd user service unit
  • remote-client-vX.Y.Z-linux-x86_64 — client
  • remote-client-gui-vX.Y.Z-linux-x86_64 — Linux client launcher GUI with embedded client mode
  • remote-client-vX.Y.Z-windows-x86_64.exe — Windows client
  • remote-client-gui-vX.Y.Z-windows-x86_64.exe — Windows client launcher GUI with embedded client mode
  • SHA256SUMS-vX.Y.Z.txt

The release binary for the server is software-only. For hardware VA-API encoding, build from source with --features hardware-encoding.

About

Rust remote desktop for Ubuntu/GNOME — XDG portal capture, H.264 encoding, QUIC transport, Linux + Windows client

Resources

License

Unknown, MIT licenses found

Licenses found

Unknown
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages