Skip to content

v2.39.0 — the sharing icon you never agreed to

Released 2026-09-24. Upgrade:

sudo snap refresh yazses     # snap
pipx upgrade yazses          # or: uv tool upgrade yazses
scoop update yazses          # Windows

If you dictate on Wayland, this is the release that stops your desktop showing a screen-sharing indicator the whole time YazSes is running.

Why it was there at all

On Wayland, typing into another window needs either ydotoold or the xdg-desktop-portal RemoteDesktop API. The portal works, and it makes your desktop display a sharing indicator while its session is open — which is a strange thing to see on an application whose entire promise is that nothing leaves your machine.

YazSes was supposed to prefer ydotoold and fall back to the portal. It could not. Three independent defects, all verified on a real Ubuntu 24.04 machine:

  1. The daemon was never installed. yazses setup installed the ydotool client; ydotoold is a separate package on Debian and Ubuntu. So setup wrote a systemd unit pointing at a binary nothing had installed.
  2. No udev rule existed anywhere in the project. /dev/uinput ships 0600 root:root, so belonging to the input group — which setup does arrange, and which the unit's own comment credited for device access — grants nothing. Installing the daemon without the rule produces a crash loop: observed at 726 restarts, each one binding its socket, printing "listening", then aborting on failed to open uinput device.
  3. The socket was looked for in the wrong place. Ubuntu ships ydotool 0.1.8, which predates --socket-path and ignores it silently: the unit passes --socket-path=%t/.ydotool_socket and 0.1.8 listens on /tmp/.ydotool_socket regardless. Readiness probed only $XDG_RUNTIME_DIR, so it answered "not ready" on a machine where ydotoold was installed, enabled and running.

So the portal was not a fallback. It was the default, on the most common Linux desktop, and nobody had agreed to it.

The portal is now offered, not taken

auto no longer selects the portal on its own. Without consent, dictation falls through to the clipboard — which delivers your words — and YazSes offers the choice instead, in the terminal, on the desktop, and in Settings → Desktop portal.

Nothing that works today is regressed. Three things already count as consent and need no migration: naming backend = "portal", an existing restore token on disk (you answered the dialog before), and the strictly confined snap, for which the portal is the only way to type on Wayland at all. A token is written on every successful portal start, so if the portal has ever worked for you, you are already covered and will not be asked again.

An explicit portal_consent = "deny" is obeyed even in the snap. Quietly doing the thing a user forbade, on the grounds that we know better, is the same paternalism as taking the portal unasked.

The indicator clears between dictations

Even when the portal is in use, its session no longer stays open for the daemon's whole life. It is released after [injection] portal_idle_release_s seconds of idle (default 60) and re-opened on demand, replaying the restore token so no dialog returns. With no token it stays open regardless — raising that dialog mid-sentence is worse than the icon.

yazses setup can now deliver the good path

It installs ydotoold and /etc/udev/rules.d/60-yazses-uinput.rules, reloads and triggers udev, and shows both in --dry-run.

Log out and back in afterwards. The group and the udev rule only reach a new session; without that the device stays unreadable and the portal is still used. The previous release's advice omitted this, which is why it did not work.

One backend table instead of five copies

inject/registry.py is now the single source of truth for which backends exist, their order, what each costs, and whether it needs consent. get_injector, yazses doctor, the config validator and the Settings combo all render the same result, so they cannot disagree — the drift that let the validator reject a backend = "ydotool" that the documentation advertised and the selector honoured. Adding a backend, or a platform, is now one row.

New command:

$ yazses inject-backend
Session: wayland    configured: 'auto'
Using:   clipboard
         instant, but a no-op in terminals and it overwrites your clipboard

  ydotool    ydotoold is not running (no socket)
             Run `yazses setup`, then log out and back in.
  portal     needs-consent
  wtype      gnome does not implement the virtual-keyboard protocol wtype needs

  portal is available but needs your permission
  Allow it:  yazses inject-backend --allow-portal

Nothing in the CLI could write [injection] backend before, so a headless or SSH user had no supported way to change it.

Also fixed

  • wtype could be chosen where it silently types nothing. It needs virtual-keyboard-manager-v1, which GNOME's Mutter and KDE's KWin deliberately do not implement. Ranking it below the portal was sufficient only while the portal was automatic; once the portal waits for consent, an unconsented GNOME user fell straight onto a backend that drops every burst. It is now reported unavailable there, with the reason.
  • A socket file is not a working daemon. Readiness also checks that our own ydotoold can open the device — and only for a user-owned socket, since one owned by root belongs to a system unit whose privileges we neither have nor need.
  • A documented config value was silently reverted. backend = "ydotool" was documented and honoured but missing from the validator's enum, so the loader replaced it with auto. The enum is now derived from the table.
  • contrib/ had already drifted from what setup writes, including the false claim about input-group membership — in a file a distro packager might install. A byte-equality test now covers the unit and the new udev rule.

Verification

Gates at release: ruff clean, mypy clean across 526 source files, 15,493 tests passed / 300 skipped, green on Linux (3.11–3.14, x86-64 and arm64), macOS, Windows and FreeBSD.

Every fix was mutation-tested — reverted to confirm its test goes red rather than passing vacuously. Several tests were also found to be reading the developer's own desktop (the portal token, /tmp/.ydotool_socket, XDG_CURRENT_DESKTOP) and passing for the wrong reason; those are now isolated.