github selkies-project/selkies 2.2.0

3 hours ago

What Selkies 2.2 brings

Selkies 2.2.0 ships with pixelflux 2.2.2 and pcmflux 2.2.1.

  • One dashboard, two layouts. The sidebar and the Wish toolbar are now layouts of the one bundled dashboard, switched live from the page. --ui-layout picks the one a page starts on, or locks one.
  • The display follows the page you use. A page of another tab or device takes its display once the owner's tab is hidden, or once its user acts through it while the owner's page sits unused. Moving from a phone to a desktop no longer leaves the desktop on the phone's picture.
  • A change reaches the stream as it lands. pixelflux 2.2.2 publishes a change at once on XShm, DRI3, and the Wayland compositor, instead of at the next frame tick. On the compositor, an application's own redraw now reaches the encoder in 2.6 ms at the median, down from 11. DRI3 now also works on NVIDIA's X driver before 595.
  • Congestion control that cuts less. WebSockets no longer cuts a desktop's stream down to its own trickle, nor sizes a window switch's backoff from the trickle before it. On a 3 Mbit/s path, those cuts now stop at 1.5 Mbit/s or above, where they used to reach 0.55 to 1.4.
  • A file manager in the Wish layout, browsing over a JSON API, with folder downloads as zip archives (#468, by @vishalkadam47).
  • Touch and keyboard: trackpad mode right-clicks on a finger held still, the on-screen keyboard rises in Edge on Windows, and every control the keyboard reaches shows its focus.

Upgrading from 2.1.0

  • The tools are selkies session, selkies resize, and selkies gpu-probe alone. The selkies-session, selkies-resize, and selkies-gpu-probe console scripts are gone. The AppImage takes session, resize, or gpu-probe as its first argument, and no longer the hyphenated names.
  • The Wish dashboard is a layout of the bundled one. --ui-layout wish (SELKIES_UI_LAYOUT=wish) serves it, and wish,default starts there with the layout toggle shown. A deployment that served the build of addons/selkies-dashboard-wish as its web root drops that root and sets the layout instead; the addon is gone.
  • /api/screenshot answers a busy or late screenshot with a 503 and Retry-After: 1, which a client retries; it used to answer 404. An unknown display stays a 404, and any other failure is a 500. Every screenshot handed out is now a screenshot event on the audit trail, with the role of the credential that took it.

Running this build

Container: the desktop, a browser, and an audio stack, with nothing to install:

docker run --name selkies -it -d --rm --shm-size=2g -p 8080:8080 \
    ghcr.io/selkies-project/selkies/desktop:2.2.0-ubuntu26.04

Open https://localhost:8080, accept the container's self-signed certificate, and log in as ubuntu with the password mypasswd. Set another with -e PASSWD= before anyone else can reach the session. debiantrixie is the other flavor, and latest now points at this release. The flags for NVIDIA, Intel, and AMD GPUs are in Getting Started.

PyPI: the wheel and the sdist, with pixelflux 2.2.2 and pcmflux 2.2.1 resolved from PyPI as well, on Python 3.9 through 3.15:

pip install selkies

AppImage: runs from wherever you put it, on any distribution with glibc 2.28 or newer. Given session as its first argument, it starts a whole session with its own display and sound server:

APP="selkies-2.2.0-$(uname -m).AppImage"
curl -O -fsSL "https://github.com/selkies-project/selkies/releases/download/2.2.0/${APP}"
chmod +x "./${APP}"
"./${APP}" --public --port=8080 --basic-auth-user=user --basic-auth-password=mypasswd

Native packages: install a private environment at /opt/selkies and put selkies on PATH:

. /etc/os-release
DISTRO="${ID}$([ "${ID}" = "ubuntu" ] && echo "${VERSION_ID}" || echo "${VERSION_CODENAME}")"
PKG="selkies-2.2.0-${DISTRO}-$(dpkg --print-architecture).deb"
curl -O -fsSL "https://github.com/selkies-project/selkies/releases/download/2.2.0/${PKG}"
sudo apt-get install -y "./${PKG}"

Native Install has the .rpm, .apk, and .pkg.tar.zst lines. Jupyter, Coder, and Open OnDemand covers selkies session and the three integrations.

Checksums and provenance: every file here is listed with its SHA-256 sum in SHA256SUMS, and GitHub attests the build provenance of each. You can check a download with either:

  • sha256sum --check --ignore-missing SHA256SUMS
  • gh attestation verify "./${PKG}" --repo selkies-project/selkies

Changes since 2.1.0

Congestion control and streaming

  • A desktop's trickle is judged by what its acknowledged frames sent. When motion started on a still screen behind a longer path, the burst not yet acknowledged made the trickle look like a stream filling the queue, and the rate was cut to the trickle's (8000 to 153 kbps on the test rig). The queue's app-limited test now compares the delivery with what the stream sent of the same frames.
  • A window switch's backoff is sized from what the path carried through the burst, not from the window's rate, which the trickle before it dilutes.
  • A queue the rate cuts leave standing is taken for the path: a longer path's or other traffic's delay becomes the floor, so the stream runs at its share rather than at the least rate.
  • A still screen's target rises no further than the capacity its path showed, and no further on a tick that measured no offer than on a trickle. A stream a hidden tab restarted is measured from its restart.
  • Each cut's log line names how late the server's own event loop ran, which tells a slow host from a slow path.
  • WebRTC: a still screen's cleanup is kept within the pace its pacers drain, a lowered video bitrate applies at once, and frames Selkies drops are reported to the encoder as withheld.
  • WebRTC's SCTP takes a TSN exactly 2^31 away as old in every comparison (found by OSS-Fuzz).

Several pages, one display

  • A display goes to the page in view once its owner's tab is hidden, and to the page its user acts through (a key, a click, a touch, a pointer move, a gamepad button, or an IME's text) once the owner's page has sat unused for three seconds. Over WebSockets, a page that joined beside an owner whose hidden tab stopped the capture used to show no video.
  • A page's size is replayed each time it owns its display, and over WebRTC the old owner's path is forgotten when a page takes the display over, as WebSockets does.
  • Both pages' tabs are logged when a controller joins or takes a display over.

Dashboard

  • One dashboard with live layout switching (by @thelamer): the sidebar and the Wish toolbar share one backend, the apps window opens fast, Wish windows drag without the 150 ms delay, and its items stay on screen when the window resizes. --ui-layout sets the starting layout or locks one.
  • One stats view and one panel catalog for every layout, each panel listed once with when it shows.
  • A file manager over a JSON API in the Wish layout (#468, by @vishalkadam47), with details, list and card views, dotfiles, folder downloads as zip, and a phone layout. Uploads go through the core's uploader into the folder shown. Archives are packed without following links, links out of the root are left unreported, listings are bounded, and responses carry no server paths or set-ID bits.
  • Readable everywhere: contrast held to WCAG AA in both themes and layouts, a focus indicator on every control the keyboard reaches, readable hover states and notices, one spacing rhythm, and a light theme. The toolbar layout is sized by its input (touch or pointer) rather than the window's width.
  • Soft keys on demand, with More keys in one colour and one edge, open or shut. The poor-connection mark sits above the toolbar's soft keys.
  • An app install or removal shows running on the button that started it until the session settles it.
  • A settings tab the server hides hands its place to the first visible one, and the layout is locked before the page connects where the server locks it.

Input, keyboard, and clipboard

  • Trackpad mode right-clicks on a finger held still and lifted, as direct touch does on a long press.
  • The on-screen keyboard rises where focusing a field does not: the dashboard asks the browser for it through the VirtualKeyboard API, which Edge on Windows needs. The floating keyboard button shows on a coarse pointer only, so it stays away while a Surface's type cover is on.
  • Gaming mode asks for its pointer lock before fullscreen spends the gesture, hands the keyboard to the stream as it starts, and releases the pointer when it ends without fullscreen. A keyboard lock the browser refuses is logged.
  • The session keeps its own clipboard when the browser hands it back. Edge on Windows wrote a KDE cut's file list back with CRLF line ends, and the paste that followed turned the cut into text; formatted copies came back rewrapped in every browser.
  • A paste waits for the PNG of an uploaded photo before the keys reach the session.

Video in the browser

  • A picture a hardware encoder padded is cut to the stream's size (by @sgtslaughta), in WebKit too, which draws a VideoFrame whole whatever its visible rectangle.
  • WebRTC moves a pinned capture before the root shrinks, and revives one that ended.
  • A WebSocket page keeps the status it reaches while its socket-open probes still run.

Desktop images and integrations

  • The base image runs the GPU probe and the resize as selkies gpu-probe and selkies resize; the Coder module and the Open OnDemand app start with selkies session, and the devcontainer feature presets the resolution with selkies resize.
  • A Wayland session compositor renders in software where the GPU refused the capture compositor, where it used to stream a flat gray screen.

Security and auditing

  • Every screenshot is on the audit trail, with the display, its size, and the role of the credential that took it. A busy or late screenshot answers 503 with Retry-After.
  • The website and the dashboard take zod 4.6.7 (GHSA-mrhx-c7xc-m3w5).

Packaging and CI

  • The old selkies-session, selkies-resize, and selkies-gpu-probe names are gone; selkies is the one console script.
  • The test tiers measure their checks on any font and either transport, and the unit tier runs inside a Selkies desktop without its settings leaking into the suites.

The capture stack

pixelflux 2.2.2 brings:

  • A change published as it lands on XShm, DRI3, and the Wayland compositor.
  • DRI3 on servers that offer DRI3 1.0 (NVIDIA's X driver before 595).
  • NvFBC held to its frame rate on a still screen, and rebuilt inside a root that shrank under it.
  • NVENC's 10-bit, 4:4:4, and constant-rate paths on older drivers.
  • Fixes to the portal host capture, the still-screen cleanup under a constant rate, VA-API (selkies-project/pixelflux#52, selkies-project/pixelflux#57), and screenshots (selkies-project/pixelflux#46).

pcmflux 2.2.1 is a maintenance release on PyO3 0.30.

Their release notes list the details for anyone using either directly.

Known limitations

  • WebRTC behind NAT may still need a STUN/TURN server; see WebRTC and Firewall Issues. The default WebSocket transport does not.
  • AV1 in WebKit: WebKit answers that it decodes AV1 and then refuses every AV1 key frame. The client treats that as a refusal and steps to H.264 without a reload.
  • WebRTC in Firefox: some Firefox releases paint WebRTC AV1 with the wrong color matrix; the FAQ says which.
  • 10-bit in Chromium: Chromium-based browsers decode AV1 over WebRTC at 8 bits only.
  • Gaming mode holds the keyboard in Chromium-based browsers, Firefox 156 and later, and Safari 26.4 and later. Brave's Shields block the Keyboard Lock API by default.
  • Hosts are Linux only. macOS and Windows support is not implemented.

Reporting

Issues go to https://github.com/selkies-project/selkies/issues, and most of the testing conversation happens on Discord. We can act on a report right away when it names:

  • the transport (websockets or webrtc);
  • the backend (X11 or Wayland);
  • the browser;
  • the GPU.

We are in need of maintainers and community contributors. Please consider stepping up, as we can never have too much help!

Contributors

There are 119 commits, none of them merges, between 2.1.0 (2026-10-21) and 9e0d8e7ce (2026-11-02), the commit this release was built from, 11 of them from Dependabot. Commits are counted by the GitHub account that authored each.

Handle Name Commits
@ehfd Seungmin Kim 102
@vishalkadam47 Vishal Kadam 4
@thelamer Ryan Kuba 1
@sgtslaughta 1

Don't miss a new selkies release

NewReleases is sending notifications on new releases.